Overview

The thinktecture DataForm is a WPF control that creates form-based user interfaces. It reflects the associated data source and the provided Data Annotations to build appropriate UI elements. You can customize the generation process by adding column mappings or item definitions. To customize the look and feel, TT.DataForm offers a wide range of properties, styles and templates.

Data Sources

TT.DataForm supports a wide range of data source types for the binding. You can bind your source via the DataSource property. The following types are supported:
  • object
  • System.Type
  • IEnumerable
  • ICollectionView
  • CollectionViewSource
  • ListCollectionView

After the binding of the data source, TT.DataForm analysis the Data Annotations that are associated with it. An annotated data source looks like this:

using System.ComponentModel.DataAnnotations; 
public class Person 
{ 
  [Required] 
  [Editable(false)] 
  [Display(Name = "ID", Description = "The id of the person.")] 
  [DisplayFormat(DataFormatString = "#{0}")] 
  public int Id { get; set; } 

  [Required] 
  [StringLength(50)] 
  [Display(Name = "Fullname",  
           Description="The full name of the person.")] 
  public string Name { get; set; } 
  … 
}
You can also put the attributes in a separate class or assign resource keys instead of text. For more information look at the appropriate MSDN section about Data Annotations.

Sub Data Sources

If the data source is associated with other entities (e.g. lookup tables), you can assign sub data sources via the SetAssociationSource() method or the SubDataSources property. In both cases you put in the name of the property and the associated data source.
For instance, if you have a data source of Person objects, like this:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Department Department { get; set; } // associated entity
}
You can bind the person list with the associated source Department like this:

var list = new ObservableCollection<Department>() 
{ 
  new Department { Id = 1, Name = "Development" } 
} 
var persons = new ObservableCollection<Person>() 
{ 
  new Person 
  { 
    Id = 1, 
    Name = "Jörg Neumann", 
    Department = departments[0] 
  } 
}; 
dataForm.DataSource = persons; 
dataForm.SetAssociationSource("Department", departments);
After that, TT.DataForm creates a ComboBox vor the Department property and binds it to the given source.

SubDataSources.png

Another way to assign sub data sources is via the SubDataSource property. This is useful, if you want to declare you source in markup and if you have a MVVM-style application. In this case you put in the name of the field and the name of the property in the view model that provides the associated data source. Example:

ViewModel:
class PersonViewModel 
{ 
  public ObservableCollection<Person> Persons { get; } 
  public ObservableCollection<Departments> Departments { get; } 
}
View:
<tt:DataForm DataSource = "{Binding Persons}"> 
  <tt:DataForm.SubDataSources> 
    <tt:SubDataSource 
      EntityPropertyName = "Department" 
      DataSourcePropertyName = "Departments"/> 
  <tt:DataForm.SubDataSources> 
</tt:DataForm>

Create Mappings

TT.DataForm offers two mapping models for the configuration of the UI creation process: control mappings and item definitions.

Control Mappings

With the control mappings you can control how TT.DataForm generates the UI elements for the given data source. By default, it follows these mapping:

Data Type UI Control
int, double, decimal NumericUpDown
string TextBox
DateTime DatePicker
bool CheckBox
ImageSource Image
others TextBlock


To customize these mapping, create ControlMapping objects and assign it via the ControlMappings property. There are different ways to define a mapping:
  • Field Mapping: create a simple field mapping that assigns a UI control for a specific field.
  • Data Type Mapping: assigns UI controls for all fields of a specific data type.
  • DataTemplate Mapping: assigns a custom DataTemplate of a specific field.

The following code shows some samples of control mappings:

<dataform:DataForm.ControlMappings> 
  <dataform:ControlMapping ControlType="{x:Type TextBox}" 
                           BindingPropertyName="Text" 
                           DataPropertyName="Id" /> 
  <dataform:ControlMapping DataType="{x:Type sys:String}" 
                           ControlType="{x:Type TextBlock}" 
                           BindingPropertyName="Text" /> 
  <dataform:ControlMapping DataType="{x:Type sys:DateTime}" 
                           IsReadOnly="True" 
                           Description="The birthday."> 
    <dataform:ControlMapping.ColumnTemplate> 
      <DataTemplate> 
        <TextBox Text="{Binding Path=Birthday, 
                        StringFormat={}{0:dd. MMMM yyyy} }" 
                        IsReadOnly="False" /> 
      </DataTemplate> 
    </dataform:ControlMapping.ColumnTemplate> 
  </dataform:ControlMapping> 
</dataform:DataForm.ControlMappings>
Optionally you can set metadata via the properties Description, IsReadOnly or IsRequired. This can be useful, if your data source doesn't provide Data Annotations or if you want to override some values.

Item Definitions

Item definitions targets the same goals as control mappings, but offers more flexibility. With item definitions you can not only configure how the edit controls are rendered, but also the labels. Beyond that, they can be defined for bound and unbound fields. So you can create elements that are not based on a property of the main data source.
You can create item definitions via DataFormItems property. It contains DataFormItem-Objects that define the UI elements via the Header and Content properties.
The following sample shows how you can define them:

<dataform:DataForm.DataFormItems> 
  <dataform:DataFormItem Header="ID:"> 
    <dataform:DataFormItem.Content> 
      <controls:NumericUpDown Width="100" 
        HorizontalAlignment="Left" 
        Value="{Binding Path=Id, ValidatesOnDataErrors=True}"/> 
    </dataform:DataFormItem.Content> 
  </dataform:DataFormItem> 
  <dataform:DataFormItem> 
    <dataform:DataFormItem.Header> 
      <TextBlock Text="Name:" /> 
    </dataform:DataFormItem.Header> 
    <dataform:DataFormItem.Content> 
      <TextBox Text="{Binding Path=Name}" /> 
    </dataform:DataFormItem.Content> 
  </dataform:DataFormItem> 
</dataform:DataForm.DataFormItems>
As you can see, the Header property can contain a simple text or a custom definition of a display control.

Styling

TT.DataForm consists of several sub components, which can be styled separately.
  • DataFormHeader: The header area of the control (title, navigation)
  • Marker: Marker symbols (active field, changed field, required field, validation error)
  • DataFormItem: A field row, incl. display control, edit control and field markers
  • DataFormFooter: The footer area of the control, incl. active field description and validation error text
  • BusyIndicator: The BusyIndicator component, that is included in the DataForm.

The following image shows you the main parts with its members (yellow) and the associated DataForm properties (green), that can be used to customize them.

TTDataFormCustomizing.png

Beyond that, you can control other display aspects with the following properties:

Property Description
RowHeight The height of a field row.
ColumnSpacing The spacing between the display and the edit control.
ColumnCount The number of layout columns.
ShowHeader Controls if the header area is displayed.
ShowFooter Controls if the footer area is displayed.
ShowEditTracker Controls if changed field markers are displayed.
ShowRowMarker Controls if changed active field markers are displayed.
ShowRequiredFieldMarker Controls if required field markers are displayed.
ShowErrorMarker Controls if error markers are displayed.
CornerRadius Sets the radius of the outer border of the control.
ShowDropShadow Controls if the outer border of the control is displayed with a drop shadow effect.

MVVM Support

To control TT.DataForm in an MVVM-style application, it provides appropriate command properties for the main functionality.
  • AddCommand: Sets an ICommand instance for adding new rows.
  • RemoveCommand: Sets an ICommand instance for removing rows.
  • SelectionButtonCommand: Sets an ICommand instance for the selection buttons of image controls.
In some situations you must call a method on the control. For example, if you want to save the changes you should fist call the AcceptChanges() method to finish the edit mode. That's a problem in a MVVM-style application, because you don't have (or want) to access the UI control directly. In that case you can use a special command, called ActionCommand. It is provided via a view model property that is bound to the view. ActionCommand provides the ExecuteAction() method, that takes a value of the DataFormAction enumeration, that defines the following actions:
  • AcceptChanges: Synchronizes the input of the active field to the data source.
  • DetachDataSource: Detaches the data source from the control.
  • Reset: Resets the UI and re-creates it.
  • SetBusy: Locks the input controls and fades in the busy indicator.
  • SetAvailable: fades out the busy indicator and unlocks the input controls.
The following code shows how you can define and use an ActionCommand:

View:
<dataform:DataForm 
  ActionCommand="{Binding ActionCommand}" />

ViewModel:
public ActionCommand ActionCommand { get; set; } 
… 
private void SaveChanges() 
{ 
  this.ActionCommand.ExecuteAction( 
    DataFormAction.AcceptChanges, null); 
  this.ActionCommand.ExecuteAction( 
    DataFormAction.SetBusy, null); 
  … 
}

The value DataFormAction.SetBusy causes the locking of the input controls and fades-in the BusyIndicator.

BusyIndicator.png

The value DataFormAction.SetAvailable resets the state.
There are additionally properties for controlling the behavior of the DataForm:

Property Description
AllowAddNew Indicates if new rows can be added.
AllowRemove Indicates if new rows can be removed.
IsReadOnly Indicates if the UI is read only.
Error Gets the current error text.
HasError Indicates if a validation error has occurred.
IsBusy Sets the control to busy/available state.
BusyIndicatorType Sets the type of the BusyIndicator. Allowed values are Wheel, Dots and Custom. Custom takes the template that are provided via the BusyIndicatorTemplate property.


Additionally, the demo project, that is included in the sources of TT.DataForm, shows a lot of aspects that are relevant in a MVVM szenario.

Last edited Jun 10, 2013 at 7:36 AM by JoergNeumann, version 15

Comments

No comments yet.