Sunday, May 1, 2011

How to build dynamic data entry forms in a WPF application?

I'm planning a WPF application which will

  • be able to create dynamic data entry forms (meaning the form gets the fields to display, their order, etc. from data in the database, not from the XAML)
  • use the MVVM pattern if possible

Here is how I plan to go about it: in a Customer Data Entry View I would set the data context:

<UserControl.DataContext>
    <vm:DynamicFormViewModel/>
</UserControl.DataContext>

and then include one element in my XAML as a placeholder for the form:

<UserControl.Content>
    <view:DynamicFormView x:Name="CustomerEntry"/>
</UserControl.Content>

then in my ModelView I want to not have static properties but I want to build the XAML as one built HTML controls back in ASP.NET, in this fashion:

View view = new View();
view.Children.Add(...)

and in this way build the Grid based on the collection of data (firstname, lastname) and meta data (field label, field name, field helptext, field display order, etc.) which the ViewModel gets from the Model.

  • has anyone built a WPF application that could create dynamic forms in this manner?
  • did you use the MVVM pattern?
  • is it possible to use the MVVM pattern in this way or does the MVVM pattern presuppose static fields in the View Model that are directly bound to static elements in the View?
From stackoverflow
  • You will have to write data templates for your various field data types so that WPF will chose how to display your data depending on its type. something of this format:

    NOTE: This is not WPF just pseudo code

    <DataTemplate DataType="{x:Type DateTime}">
      <DatePicker Value="{Binding}"/>
    </DataTemplate>  
    <DataTemplate DataType="{x:Type String}">
      <TextBox Text="{Binding}"/>
    </DataTemplate>
    

    It doesn't have to be a primitive type. It can be an Email, DateApproved or even a Url class type. e.g.

    class Customer  
    {
       public Email Email{get;set;}
       public DateTime DateApproved{get;set;}
       public URI Url{get;set;}
    }
    
    public class Email 
    {
       public string Type{get;set;}
       public string Value{get;set;} 
    }
    

    ..etc...

    Update

    Check out this WPF Dynamic UI example on MSDN: Dynamic Data Entry with WPF and LINQ

    Edward Tanguay : Right but I want to include custom types such as Email, URL, Duration, Customer so each will have to have its own validation. I'm imagining a base called called "CustomType" which all these types inherit and each builds its own method called e.g. IsValid() and displayAsXaml() etc.
    Edward Tanguay : Putting things in XAML to me feels to static. I remember back in ASP.NET apps, in almost every project you ended up having a tiny little Default.aspx with a couple controls in it which were all dynamically created. Otherwise you create one-time, static applications which can't be changed very easily. I suppose to get this dynamic nature out of WPF you will also have justn a tiny Window1.xaml and then eveyything being created dynamically in the same way.
  • You need to setup a DataTemplate for each field type eg Date, String, Bool. This will determine how each field will appear.

    You could then use the columns for a database query to generate a list of objects and place them into an ItemsControl.

    ObservableCollection<ColumnDef> columns = new ObservableCollection<ColumnDef>();
    
    // Add columns from DB
    columns.Add(new StringColumnDef{Object=..., Field=..., Label=..., Value=...});
    columns.Add(new DateColumnDef{Object=..., Field=..., Label=..., Value=...});
    
    items.ItemsSource = columns; // items is an ItemsControl
    

    Each item in the item control will display based on the DataTemplate for that type.

    Inside the ColumnDef you could use Reflection to update the data object with changes from the UI controls. You can then apply the changes to the databae when the user saves.

  • Have a look at the DataForm from the Silverlight toolkit as a baseline! Most of the Silverlight stuff can actually be compiled in WPF!

  • By default, data binding occurs only when the target UI element is initialized. This is called one-time binding. For most purposes, this is insufficient; with two-way binding, changes to the source are automatically propagated to the target, and changes to the target are automatically propagated to the source.

0 comments:

Post a Comment