Thursday, May 20, 2010

Committing bound cell changes immediately in WPF Datagrid

The WPF DataGrids default mode of operation is committing all changes on a per-row edit instead of a per-cell edit. In this post we will see how to change that behavior to do a per-cell commit regardless of the bound object's capabilities.

But first a little bit about the workings of the DataGrid.
When binding data in a WPF DataGrid it holds all changes to the data in an intermediary object. If the user edits a cell the changes will be done only in this intermediary object, the exception being if the bound object does implement IEditableObject in which case the DataGrid uses the object's edit capabilities instead.
But if we do not have an IEditableObject to bind to all changes made inside the row will only be committed to the bound object once the row edit ends. If we still need to commit on a cell by cell basis we need to attach a handler to the CellEditEnding event of the grid and use the following code in the handler.

private bool isManualEditCommit;
private void HandleMainDataGridCellEditEnding(
  object sender, DataGridCellEditEndingEventArgs e) 
{
 if (!isManualEditCommit) 
 {
  isManualEditCommit = true;
  DataGrid grid = (DataGrid)sender;
  grid.CommitEdit(DataGridEditingUnit.Row, true);
  isManualEditCommit = false;
 }
}

This will update the bound object whenever a cell edit is ending, i.e. whenever the cell looses focus.

Submit this story to DotNetKicks

14 comments:

Anonymous said...

Thanks very much for posting. I'm using the code in a Window to display an editable matrix. The user edits any cells before closing. Therefore you code is also called from Window closing.

Private isManualEditCommit As Boolean

Private Sub MainWindow_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing
CommitEdit()
MsgBox(dt.Rows(0)("Column1") & " " & dt.Rows(0)("Column2"))
End Sub

Private Sub DataGrid1_CellEditEnding(ByVal sender As Object, ByVal e As System.Windows.Controls.DataGridCellEditEndingEventArgs) Handles DataGrid1.CellEditEnding
CommitEdit()
End Sub

Sub CommitEdit()
If Not isManualEditCommit Then
isManualEditCommit = True
DataGrid1.CommitEdit(DataGridEditingUnit.Row, True)
isManualEditCommit = False
End If
End Sub

Christopher Theunissen said...

You also should only commit when e.EditAction is DataGridEditAction.Commit, otherwise pressing Cancel will not undo the current change.

Anonymous said...

Thanks a whole bunch ! I been struggling with the issue for the past few days. Your solution together with Christopher's is a great help to me.

Orson said...

perfect! thanks

Anonymous said...

This is beautiful. Using this allowed me to easily get a reference to actual datarows and make additional updates to the values, without having to do some really messy "ItemContainerGenerator.ContainerFromIndex(e.Row.GetIndex)" stuff. Thank you!

Carnifex said...

Hi, you write [...] if the bound object does implement IEditableObject in which case the DataGrid uses the object's edit capabilities [...].
I've collection of objects implementing IEditableObject, but DataGrid still using "per-row edit" and I don't see changes immediately. What I have to do to use IEditableObject capabilities? Where (in the code) DataGrid (or CollectionView) check and decide to use intermediary object instead of IEditableObject?

Anonymous said...

GREAT!!!
THX

dashingsidds said...

Need to call "Keyboard.ClearFocus()" at the end. Otherwise very good

Giri Prasad said...

hi,

after celleditendingevent, i want to set the focus on the next cell of the selected row, do u have suggestion for this problem.

Anonymous said...

Thank you for your article. I'm using this handler with the CellEditEnding="HandleMainDataGridCellEditEnding" of my WPF DataGrid.
I'm also setting TAB and SHIFT+TAB functionality to my grid, in the GridView_PreviewKeyDown.
When pressing the TAB, I'm moving focus to the NEXT cell, and when pressing the SHIFT+TAB I'm moving focus to the PREVIOUS cell.
I have an event handler with each grid-cell, where each cell is of type TextBox. This handler is called TextBox_PreviewLostFocus.

When using TAB, the functionality is as expected (i.e. HandleMainDataGridCellEditEnding is executed after TextBox_PreviewLostFocus).
However when pressing SHIFT+TAB, "HandleMainDataGridCellEditEnding" is not being executed after TextBox_PreviewLostFocus.

Does your handler need to be called from any other specific place, since on SHIFT+TAB, CellEditEnding is not called.

Thanks.

Anonymous said...

What if focus is still exist in the respective cell which is a text box?

Anonymous said...

Thanks, it resolved my problem.

Marcoexo said...

Thanks tou saved me ! I upgraded from wpf Toolkit DataGrid to 4.X DataGrid and didn't understand why propertychanged was not issued on selecting a cell on same row ;-)

Godd job Bro'!

Anonymous said...

Thanks a lot dude cheers