简体   繁体   中英

WPF DataGrid column header mouse events

I'm trying to extend standard DataGrid functionality by adding some methods and properties into a derived control:

public class ExtendedGrid : DataGrid
{
    ...
}

However, handling mouse events of the headers is still unclear to me. Since DataGridColumnHeader is not a part of the visual tree (correct me if I mistake), and the only way to set an event handler to the MouseEvent is to apply style on it ( How do I capture "Click" events on a DataGrid column headers ).

Since I'm extending the DataGrid , I'd like to keep implementation in code, without adding any XAML, in my opiinion it's inconsistent in terms of code readability. Therefore, I wrote the following code:

private void InitializeStyles()
{
    Style headerStyle = new Style(typeof(DataGridColumnHeader));
    headerStyle.Setters.Add(new EventSetter(MouseDownEvent, new MouseButtonEventHandler(OnColumnHeaderMouseDown)));
    foreach(var column in Columns)
    {
        column.HeaderStyle = headerStyle;
    }
}

This code is called on AutoGeneratedColumns and doesn't work well (eg at all). Even if it would, it would impose limitations on setting styles on headers.

Is there a way to handle DataGridColumnHeader mouse events in my ExtendedGrid without any XAML? Or XAML styles is anyway better than search of workarounds (and probably overcomplicationg things)?

DataGridColumnHeader is of course part of the visual tree. It's the DataGridColumn and its descendants that are not part of the visual tree as they serve as data objects for the actual rendered CellTemplate or HeaderTemplate .

Note that since DataGridColumnHeader extends ButtonBase , you can also execute an ICommand that is assigned to DataGridColumnHeader.Command .

If you want to execute the same event handler for a certain event eg, Click for each DataGridColumnHeader eg, in order to execute a column based sort, then you can simply register a handler for the routed event directly on the DataGrid :

<DataGrid DataGridColumnHeader.Click="SortColumn_OnClick" />

or when using a Style set the DataGrid.ColumnHeaderStyle :

<DataGrid>
  <DataGrid.ColumnHeaderStyle>
    <Style TargetType="DataGridColumnHeader">
      <EventSetter Event="Click" 
                   Handler="SortColumn_OnClick" />
    </Style>
  </DataGrid.ColumnHeaderStyle>
</DataGrid>

If you are looking for a specific DataGridColumnHeader of a column or columns, then traverse the visual tree to find them.
You can get the DataGridColumnHeader of a certain column by handling the DataGrid.Loaded event:

MainWindow.xaml

<DataGrid Loaded="DataGrid_OnLoaded" />

MainWindow.xaml.cs

private void DataGrid_OnLoaded(object sender, RoutedEventArgs e)
{
  var dataGrid = sender as DataGrid;
  foreach (DataGridColumn dataGridColumn in dataGrid.Columns)
  {
    if (TryFindColumnHeader(dataGrid, dataGridColumn.DisplayIndex, out DataGridColumnHeader columnHeader))
    {
      columnHeader.Click += (sender, args) => MessageBox.Show($"Column #{columnHeader.DisplayIndex} Header clicked");
    }
  }
}

public bool TryFindColumnHeader(
  DependencyObject dataGrid,
  int columnIndex,
  out DataGridColumnHeader dataGridColumnHeader)
{
  dataGridColumnHeader = null;

  for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(dataGrid); childIndex++)
  {
    DependencyObject childElement = VisualTreeHelper.GetChild(dataGrid, childIndex);

    if (childElement is DataGridColumnHeader columnHeader && columnHeader.DisplayIndex.Equals(columnIndex))
    {
      dataGridColumnHeader = columnHeader;
      return true;
    }

    if (TryFindColumnHeader(childElement, columnIndex, out dataGridColumnHeader))
    {
      return true;
    }
  }

  return false;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM