WPF DataGrid Sample: Locking input to the row being edited

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.

DataGridSample_LockingRowInEditMode.zip

Comments

  • Anonymous
    December 18, 2008
    PingBack from http://blog.a-foton.ru/index.php/2008/12/18/wpf-datagrid-sample-locking-input-to-the-row-being-edited/

  • Anonymous
    January 05, 2009
    Why isn't "IsEditing" already a property on DataGrid?  I think this functionality would be very useful. My problem is that I am hosting a WPF DataGrid on a WinForms dialog.  The user wants to dismiss my dialog with the ESC key, which the DataGrid already uses to cancel row/cell editing. In my dialog, I listen for the KeyUp event, and if the DataGrid is not editing, then I should close my dialog.  However, the only way I can tell if the DataGrid is editing is to also listen for BeginEditing and RowEndEditing.  Seems like a simple property that could be on the DataGrid itself.

  • Anonymous
    January 06, 2009
    The comment has been removed

  • Anonymous
    April 10, 2009
    UPDATE: the WPF DataGrid v1 has just released. For more information, see this post . The information

  • Anonymous
    November 17, 2009
    This is good but the focus is lost when entering edit mode. Example. If you select the City column and start typing using the keyboard. The cell goes into edit mode and the first character is entered in the the textbox, but then you have to press tab to get focus to type futher. The same applies for entering edit mode using the mouse. An extra mouse click is required.