5 More Random Gotchas with the WPF DataGrid

1. DataGridColumn.SortDirection does not actually sort the column.

DataGridColumn.SortDirection is used to queue the visual arrow in the DataGridColumnHeader to point up, down, or to not show. To actually sort the columns other than clicking on the DataGridColumnHeader, you can set the DataGrid.Items.SortDescriptions programmatically.

Here is an example of an ICommand used to trigger grouping of a particular column in the DataGrid. In addition to grouping it, I also sort it on the same column name that I grouped it with. Note that this code will programmatically sort the column but will not update the sorting arrow since I did not update SortDirection.

public ICommand GroupColumn

{

  get

  {

      if (_groupColumn == null)

      {

          _groupColumn = new RelayCommand<object>(

           (param) =>

           {

           // the delegate is passed in the column header

           // name which matches the Binding on the column

           string header = param as string;

           //

           // SearchResults is the ItemsSource that DataGrid

           // is bound to. Below I programmatically group and

           // sort the collection based on the header name

           //

           SearchResults.GroupDescriptions.Add(

                 new PropertyGroupDescription(header));

           SearchResults.SortDescriptions.Add(

                 new SortDescription(header, ListSortDirection.Ascending));

           });

      }

      return _groupColumn;
}

}

 

 

2. DataGridColumns cannot be styled as they are not FrameworkElements.

DataGridColumn derives from DependencyObject, not FrameworkElement, and therefore cannot be styled like many of the other elements on DataGrid. You can think of a DataGridColumn as data storage for what each cell’s content will be template with and a couple other properties like the width of each cell and how it is sorted. They are not meant to be visual elements like DataGridCell or DataGridRow.

 

3. While editing a row, the row is committed by pressing Enter, pressing tab on the last cell of the row, losing focus, clicking the column header to sort the column, programmatically calling DataGrid.CommitEdit, updating DataGrid.CurrentCell to a cell outside the current row, or updating DataGrid.CurrentItem.

The actions described above are the default actions to commit a row. One action that deserves a little more clarification is the commit on losing focus. Each DataGridCell listens for changes to its IsKeyboardFocusWithin property and when it changes it updates the CurrentCell accordingly (which causes the row to commit).

 

4. Disable commit behavior through the DataGrid.RowEditEnding event

In the previous gotcha I explain all the ways that a row can be committed. If you want to customize that behavior, the best way to do it is through DataGrid.RowEditEnding. This event is fired at the start of any and all row commits and you have the ability to cancel the commit behavior in this event through DataGridRowEditEndingEventArgs.Cancel. See more about editing behavior here.

 

5. Customize focus behavior after a row commit through the DataGrid.RowEditEnding event

While there isn’t a DataGrid.RowEditEnded at this time (March 2009 Release), you can use the Dispatcher trick to get custom behavior after a row commit has occurred. In DataGrid.RowEditEnding, you can specify a Dispatcher.BeginInvoke so that the code in the delegate is executed after the row commit has completed. Here is some example code which sets focus to the second cell of the next row and puts it in edit mode but only when I press tab on the last cell of the row before the NewItemPlaceholder.               

private void OnRowEditEnding(object sender, DataGridRowEditEndingEventArgs e)

{

  if (e.EditAction == DataGridEditAction.Commit)

  {

    //

    // custom commit action:

    // tab specific behavior for editing workflow.

    // moves to the next row and opens the second cell for edit

    // if the next row is the NewItemPlaceholder

    //

    if (_isTabPressed &&

        e.Row.Item == dataGrid.Items[dataGrid.Items.Count - 2])

    {

      // set the new cell to be the last row and the second column

      int colIndex = 1;

      var rowToSelect = dataGrid.Items[dataGrid.Items.Count - 1];

      var colToSelect = dataGrid.Columns[colIndex];

      int rowIndex = dataGrid.Items.IndexOf(rowToSelect);

      // select the new cell

      dataGrid.SelectedCells.Clear();

      dataGrid.SelectedCells.Add(

        new DataGridCellInfo(rowToSelect, colToSelect));

      this.Dispatcher.BeginInvoke(new DispatcherOperationCallback((param) =>

      {

          // get the new cell, set focus, then open for edit

          var cell = Helper.GetCell(dataGrid, rowIndex, colIndex);

          cell.Focus();

          dataGrid.BeginEdit();

          return null;

      }), DispatcherPriority.Background, new object[] { null });

    }

  }

}

 

See more WPF DataGrid gotchas.