Compartilhar via


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.

Comments

  • Anonymous
    April 07, 2009
    PingBack from http://asp-net-hosting.simplynetdev.com/5-random-gotchas-with-the-wpf-datagrid/

  • Anonymous
    April 07, 2009
    You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Anonymous
    April 07, 2009
    Dear Vincent, A very useful summary indeed. If possible, could you also sometime summarize the intended commit behavior at row level ... or have it explained in the forthcoming documentation ? With regards to a row what is the standard datagrid behavior and its possible adaptations, assuming NewItemPlaceholderPosition.AtEnd:

  • (1) when creating a new row :
    • (a) is the row committed by 'enter' and/ or by 'tab' (*) ?
    • (b) if the row is committed only by 'enter' (e.g.), is it possible to commit it with 'tab'; if yes what is the recommended manner of doing ? (previewkeydown override 'tab' to 'enter' in the last editable cell ?)  (c) if the user uses another key like 'arrow up/down/right/left' or 'skip/tab', should the row loose focus and commit as well ?
    • (d) when entering the next row after commit, the standard behavior is for the datagrid to set focus in the same column; is it ok to force the first (or any other) editable cell to get focus;  if yes what is the recommended manner of doing ?
  • (2) same questions when updating an existing row Thanks so much, Julie (*) 'tab' means in this context that the datagrid will, when on the last possible editable cell (brought into view), skip over any 'IsReadOnly' column (even invisible and virtualized), out of the row to the next row
  • Anonymous
    April 10, 2009
    UPDATE: the WPF DataGrid v1 has just released. For more information, see this post . The information

  • Anonymous
    April 14, 2009

  1. DataGridColumn.SortDirection does not actually sort the column. DataGridColumn.SortDirection is used
  • Anonymous
    April 14, 2009
    Julie, Does this post, http://blogs.msdn.com/vinsibal/archive/2009/04/14/5-more-random-gotchas-with-the-wpf-datagrid.aspx, answer your questions regarding commit behavior?

  • Anonymous
    July 06, 2009
    How to wrap tooltip in datagridcell according to width of DatagridtextColumn

  • Anonymous
    December 24, 2009
    I have a WPF toolkit datagrid with "AutoGenerateColumns" as TRUE. I am also using MVVM design (Grid in the view and it's the logic in the ViewModel). How can I add/edit/delete/cancel the data in the datagrid (handle the events)??