5 Random Gotchas with the WPF DataGrid

Since the release of the WPF DataGrid there have been several common patterns of questions that developers were asking on the discussion list. I thought that I would capture some of that here so it would be easy to find. I'll also try to keep it as short as possible and refer to other links for more information. These gotchas are in no particular order.

 

1. DataGridColumns are not part of the visual tree.  

Since a DataGridColumn has a Binding DP it is easy to think that bindings to other DataGridColumn DPs will work just the same but they don't. Below is an example showing a DataGridCheckBoxColumn setting the Binding DP and CanUserSort through data binding. While Binding will bind correctly, CanUserSort will not.                                        

<dg:DataGridCheckBoxColumn Header="Selection"

                           Binding="{Binding IsSelected}"

                           CanUserSort="{Binding CanSort}"/>

 

Note that the reason Binding does work is because the internal code dynamically sets the binding of DataGridColumn.Binding to the DataGridCell.Content. It does not do so for the other DataGridColumn DPs.

Workaround => Check out Jaime’s post on how to get the DataGridColumns to use the same DataContext as the DataGrid.

 

2. DataGridColumn.Binding will automatically coerce the binding to BindingMode.TwoWay and UpdateSourceTrigger.Explicit

No matter how you declare the binding for DataGridColumn.Binding, internally it will coerce the BindingMode to TwoWay and UpdateSourceTrigger to Explicit (even if you set them explicitly yourself). This is because the DataGrid manages the binding operations itself for editing functionality. Now, you might run into an issue when you have a read-only property with a TwoWay binding mode. If you set DataGridColumn.IsReadOnly to true, then the BindingMode will not be coerced to TwoWay.

 

3. You manage the bindings when using a DataGridTemplateColumn.

The DataGrid internal code does not coerce the bindings set on DataGridTemplateColumn because DataGridTemplateColumn doesn’t even have a Binding DP. Since you are setting the bindings up yourself, you have to tell it when to commit. One common way to do this is to setup a OneWay binding on the UIElement in the CellTemplate and a TwoWay binding on the CellEditingTemplate. See this previous post for example code (under “Template Columns”). The example code uses an UpdateSourceTrigger of PropertyChanged. If that does not suite your needs for committing the data, you can mimic what the DataGrid internal code does and update the source yourself. Set the UpdateSourceTrigger to Explicit and listen to the DataGrid.CellEditEnding event. You can then update the source explicitly in that event.

 

4. Use DataGridColumn.ElementStyle and DataGridColumn.EditingElementStyle to set properties on a cell’s Content.

Each stock DataGridColumn type produces a specific type of Control as the content for each DataGridCell in the column. See this previous post for more info on that. It seems that the first instinct to set properties on the cell’s content is to set them through the DataGridCell Style when they should instead be set on DataGridColumn.ElementStyle and/or DataGridColumn.EditingElementStyle. ElementStyle is the Style applied to the non-editing element. EditingElementStyle is for the editing element.

In a previous gotcha, I mention that DataGridColumns are not part of the visual tree. With that said, you can still create bindings that map to the same DataContext as DataGridColumn.Binding on DPs from a Style set on DataGridColumn.ElementStyle and DataGridColumn.EditingElementStyle. That is because these styles are applied dynamically to the cell’s content which has the correct DataContext. Below I am setting a DataGridTextBoxColumn’s ElementStyle. Notice that I have a binding setup on Background with a “MyBrush” property path. I am assuming that property is coming from the DataContext of the DataGridCell (which is the same DataContext of the DataGridRow).                                

<dg:DataGridTextColumn Header="First Name"

            Binding="{Binding Path=FirstName}">

  <dg:DataGridTextColumn.ElementStyle>

      <Style TargetType="{x:Type TextBlock}">

        <Setter Property="ToolTip" Value="{StaticResource TextToolTip}" />

        <Setter Property="Background" Value="{Binding MyBrush}" />

        <Setter Property="TextWrapping" Value="Wrap" />

      </Style>

  </dg:DataGridTextColumn.ElementStyle>

</dg:DataGridTextColumn>

 

 

5. Data source items should implement IEditableObject to get transactional editing functionality.

If you are not familiar with IEditableObject, see this MSDN article which has a good explanation and code sample. The DataGrid has baked in functionality for transactional editing via the IEditableObject interface. When you begin editing a cell, the DataGrid gets into cell editing mode as well as row editing mode. What this means is that you can cancel/commit cells as well as cancel/commit rows. For example, I edit cell 0 and press tab to the next cell. Cell 0 is committed when pressing tab. I start typing in cell 1 and realize I want to cancel the operation. I press ‘Esc’ which reverts cell 1. I now realize I want to cancel the whole operation so I press ‘Esc’ again and now cell 0 is reverted back to its original value.

 

See more WPF DataGrid gotchas.