DataGrid Productivity Features Part Two – A Flexible Association Editor

Following on from the previous post about the use of the PropertyEditor class in a DataGrid to reuse one template across multiple columns, today I’ll discuss a more sophisticated example based off a common data grid use case.

It’s not unusual for the data source of a DataGrid to be a set of entities loaded from a database, often using an object-relational mapper such as our own Lightspeed. This is a convenient and compact way to display and edit a database in a simple tabular view.

But what happens when users want to edit associations using the data grid? For example, an Attendant is associated with a Zoo; what if we want to reassign the Attendant to a different Zoo? Databases represent this using a foreign key, but editing a foreign key sucks as a user experience. A better approach would be to use a ComboBox, which the user can drop down to see all the available Zoos — identified by their Name property — and select the desired one for the Attendant.

This is easy enough if you’re only interested in one association: you create a DataTemplate with a ComboBox, set up the ItemsSource to be the list of Zoos, point the DisplayMemberPath at the Name property, set the SelectedValuePath to the Id property, and off you go.

But most models involve multiple associations. And it would be really boring to have to create a separate DataTemplate for each association. All those DataTemplates would be very similar, but with different settings for ItemsSource, DisplayMemberPath, etc. What we’d like to do is create a single DataTemplate with all the plumbing, and parameterise it with those different settings.

Just like in the previous example, PropertyEditors provide a fast way to accomplish this — create a quick class that holds the properties to be displayed and edited, set an instance of this class to be the PropertyEditor’s EditContext, and then bind to these properties in a DataTemplate that provides the ComboBox.

An example of this is shown below, where the user can select a product from the ComboBox by its Name property, and the corresponding Sku (code) is set to be the SelectedValuePath. Further column editors can be added, and the template reused by passings its own appropriate values unique to the column (for instance Supplier and its corresponding ID).

The code-behind in this example contains several model classes. The first, Fruit, simply contain two properties – FruitName and Sku (its code). The model for the DataGrid has several properties including Product which is of type integer – there is a link to the full code at the bottom of this post. The final required class is the trivial ‘edit context’ class which will hold the properties that the particular column’s ComboBox will display and edit. For this scenario it also requires a reference to the collection that will populate the ItemsSource of the ComboBox.

public class FruitEditInfo
{
  public string NameProperty { get; set; }
  public string ValueProperty { get; set; }
  public IList<Fruit> ItemsSource { get { return new FruitList().fruitList; } }
}

There are three parts required in the XAML markup to create the above example. Setting up the PropertyEditor is the first and it lives inside the DataGrid tag.

<ms:DataGrid ItemsSource="{Binding Data}">
  <ms:DataGrid.Editors>
    <ms:PropertyEditor PropertyName="Product"
                       EditorTemplate="{StaticResource AssociationSelector}" >
      <ms:PropertyEditor.EditContext>
        <local:FruitEditInfo NameProperty="FruitName" ValueProperty="Sku" />
      </ms:PropertyEditor.EditContext>
    </ms:PropertyEditor>
  </ms:DataGrid.Editors>
</ms:DataGrid>

The next part is to create the AssociationSelector data template in the resource dictionary, which is applied as the EditorTemplate above.

<DataTemplate x:Key="AssociationSelector">
  <ComboBox DisplayMemberPath="{Binding EditContext.NameProperty}"
            SelectedValuePath="{Binding EditContext.ValueProperty}"
            ItemsSource="{Binding EditContext.ItemsSource}"
            SelectedValue="{Binding Value, Mode=TwoWay}" />                  
</DataTemplate>

As you can see, the ComboBox binds to the appropriate EditContext properties, which we set by creating an instance of our ‘edit context’ class then setting their values based on what data the column will display and edit. I think you’ll agree each column having its own PropertyEditor is much nicer than duplicating the above DataTemplate and just replacing the DisplayMemberPath/SelectedValuePath!

One more thing is required for this example, and that’s a simple converter that takes the integer representation displayed by the ComboBox and returns the associated name of type string. A real-world implementation would of course do something much more useful with the Id such as using it to update an entity.

Here’s an archive of the finished demo. To get it working just add a reference to your own copy of Mindscape.WpfElements.dll.

DataGridPropertyEditor.zip

All of this functionality is available in the Express Edition of WPF Elements 5, so if you don’t have it already head over to the download page and grab the 60 day free trial, and if you have any questions feel free to comment or make a post in the forums. Until next time!

Tagged as WPF Elements

Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top