Awesome DataGrid Productivity Features – DataTemplate Reuse With PropertyEditors

An interesting case came up in the forums this week regarding the use of ComboBoxes as editors inside DataGrid cells, and whether they can be used to select a particular object by ID in the background while displaying a corresponding Description property on the object instead of the ID. This could of course be implemented with a DataTemplate containing a ComboBox with converters to display the appropriate description, but this approach would get messy as you’d have to add a unique template and converters for each column which needed a ComboBox. Wouldn’t it be much more elegant if you could create just one template but parameterise it so you could pass in different options for each column that used it?

Well, you can! The PropertyEditor class supports an EditContext property which you can set up on a per-property basis, and the editor DataTemplate can bind to this EditContext and customize the template based on its state. I’ll go into detail on the above case in the second part of this post, but first off let’s look at a simpler use case where the EditContext can be used to clean up your XAML, but which is a bit easier for demo purposes!

Imagine a case where we have a model with multiple integer properties representing specifications of some object, for instance personal computers. We’d like to display these in a DataGrid, and it’d be great if the user could change the value in a cell with a Slider (or UpDown control, etc). As the various properties have differing sensible ranges they would normally need their own template containing a unique Slider with its Minimum/Maximum/SmallChange set appropriately.

Instead of creating three separate templates we can set the EditContext of a PropertyEditor, and then bind to this from the template. Firstly, we’ll need a class to hold the values we need to vary between instances of the template. For our numeric editing example this is very simple:

public class ComputerEditInfo
{
  public int Maximum { get; set; }
  public int Minimum { get; set; }
  public int SmallChange { get; set; }
}

Then back in the XAML inside the DataGrid declaration, open a PropertyEditor tag and set its EditContext to be an instance of the above class. Here’s two out of three required:

<ms:DataGrid.Editors>
  <ms:PropertyEditor PropertyName="Ram"
                     EditorTemplate="{StaticResource NumericSliderCellTemplate}" >
    <ms:PropertyEditor.EditContext>
      <local:ComputerEditInfo Minimum="1" Maximum="8" SmallChange="1" />
    </ms:PropertyEditor.EditContext>
  </ms:PropertyEditor>
 
  <ms:PropertyEditor PropertyName="HardDrive"
                     EditorTemplate="{StaticResource NumericSliderCellTemplate}">
    <ms:PropertyEditor.EditContext>
      <local:ComputerEditInfo Minimum="120" Maximum="1024" SmallChange="120" />
    </ms:PropertyEditor.EditContext>                    
  </ms:PropertyEditor>
</ms:DataGrid.Editors>

Notice that we have to use the Editors collection instead of the Columns collection.

Now we can create our reusable DataTemplate. There are a couple of important things to be aware of when creating a reusable DataTemplate for use with a PropertyEditor.

First, because the template is going to be used for multiple properties, you can’t bind to the property by the actual property name on the originating object. Instead, the PropertyEditor plumbing presents the value being edited as Value, and that’s what you have to bind to in the editing control(s).

Second, the PropertyEditor plumbing also supplies an EditContext property that you can bind to. For any given instance of the editor template, this will contain the value of the EditContext in the PropertyEditor declaration that applies to the current instance.

Here’s the editor template for the numeric properties of the computer object:

<DataTemplate x:Key="NumericSliderCellTemplate">
  <Grid VerticalAlignment="Center" >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding EditContext.Minimum}"
               Margin="2,0,0,0"
               Foreground="#3D3D3D" />
    <Slider Value="{Binding Value, Mode=TwoWay}"
            Minimum="{Binding EditContext.Minimum}"
            Maximum="{Binding EditContext.Maximum}"
            SmallChange="{Binding EditContext.SmallChange}"
            Margin="2,0,2,0"
            Grid.Column="1"/>
    <TextBlock Text="{Binding EditContext.Maximum}"
               Grid.Column="2"
               Margin="0,0,4,0"
               Foreground="#3D3D3D" />
  </Grid>
</DataTemplate>

Notice how the Slider and the TextBlocks bind to the EditContext to implement and display the correct per-property settings for minimum and maximum values and change size. One template for multiple columns, each with their values correctly set – that’s pretty clean! And here’s the output, where after the cell is double-clicked into edit mode the Slider appears set with the minimum and maximum.

This of course can be extended for passing in any sort of data that can be reused with adjustments across columns, and next time we’ll look at a detailed implementation of the ComboBox case, where its DisplayMemberPath and SelectedValuePath will update based on the various PropertyEditors set for the columns. Check out our site for more information on the DataGrid and the 50+ other controls included in WPF Elements 5. Until then!

Tagged as WPF Elements

One Response to “Awesome DataGrid Productivity Features – DataTemplate Reuse With PropertyEditors”

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top