Scenario

By default the WPF DataGrid will commit a row when focus is lost on the row, the ‘Enter’ key is pressed, tabbing to the next row, or programmatically calling commit on the row.  Let’s say I want to control how the user commits a row edit by locking input to just that row being edited and having a filtered set of input or a single input of my choosing that will commit that row. 

Implementation

There are no configuration APIs on the DataGrid that allow you to tell it what input will commit a row, but there are events that allow you to cancel editing operations as you so choose.  So we can work around that.  Another thing we will have to do is keep track of the editing state of the DataGrid.  With this extra flag, we can also use it to disable other rows while the current row is being edited. 

You can subclass DataGrid and add functionality that may be more generalized but for this sample I’m just going to use a UserControl.  Here is the flag that I will use which is a DP so it can be used in the DataGridRow style.

public partial class CustomDataGrid : UserControl

{    

  public bool IsEditingRow

  {       

    get { return (bool)GetValue(EditingRowProperty); }

    set { SetValue(EditingRowProperty, value); }

  }

 

  public static readonly DependencyProperty EditingRowProperty =

      DependencyProperty.Register(

        "IsEditingRow",

        typeof(bool),

        typeof(CustomDataGrid),

        new FrameworkPropertyMetadata(false, null, null));

}

 

This flag is going to be used such that anytime a row is being edited, other rows will be disabled and you can only commit through an input that I choose.  To disable the rows we can add something like this in the row’s style:

<Style TargetType="{x:Type dg:DataGridRow}">

  <Style.Triggers>

    <MultiDataTrigger>

      <MultiDataTrigger.Conditions>

        <Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CustomDataGrid}}, Path=IsEditingRow}" Value="True" />

        <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEditing}" Value="False" />

      </MultiDataTrigger.Conditions>

      <Setter Property="IsEnabled" Value="False"/>

      <Setter Property="Foreground" Value="LightGray"/>

    </MultiDataTrigger>

  </Style.Triggers>

</Style>

 

For naming purposes I probably should have called it IsInEditMode to differentiate but please indulge me for now.  So anyway, if the DataGrid is editing a row and the particular row is not in edit mode it will be disabled.

Now, for the logic, two important events for this scenario are:

·         DataGrid.BeginningEdit

·         DataGrid.RowEditEnding

BeginninigEdit will be used to set the IsEditingRow flag as well as cancel a BeginEdit operation when trying to open a different row for edit while still editing the current row.

private void DataGrid_Standard_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)

{       

  if (!IsEditingRow)

  {
    // set edit mode state

    IsEditingRow = true;

    _currentEditingRow = e.Row;
  }

  else if (e.Row != _currentEditingRow)

  {

    // cancel all new edits for different rows

    e.Cancel = true;

  }

}

 

RowEditEnding will be used to cancel a commit if IsEditingRow is true.  It also resets when the row edit is cancelled.

private void DataGrid_Standard_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)

{       

  if (e.EditAction == DataGridEditAction.Cancel)

  {

    // cancelling the entire row will reset the state

    IsEditingRow = false;

  }

  else if (e.EditAction == DataGridEditAction.Commit)

  {

    e.Cancel = IsEditingRow;

  }

}

 

So for this workaround, it is really based on how you want to manipulate IsEditingRow.  For this sample, I want to commit a row only when the ‘Enter’ key is pressed.  I can do that by listening to the PreviewKeyDown event and setting IsEditingRow to false for the ‘Enter’ key.

private void DataGrid_Standard_PreviewKeyDown(object sender, KeyEventArgs e)

{       

  DataGridRow row = GetRow(DataGrid_Standard, DataGrid_Standard.Items.IndexOf(DataGrid_Standard.CurrentItem));

  if (row.IsEditing && e.Key == Key.Enter)

  {

    // only 'Enter' key on an editing row will allow a commit to occur

    IsEditingRow = false;

  }

}

 

That’s basically it.  If you try out the sample you will see that when you open a row for edit, the rest of the rows will be disabled and the only way to commit is through the ‘Enter’ key.

LockingEditMode

Let’s say ‘Enter’ to commit is not the functionality you want.  Instead you want to commit using a specific button.  In that case you can listen to the click event or setup a command and set IsEditingRow accordingly based on your specific conditions. 

You can download the full sample here.