【问题标题】:WPF DataGrid column header mouse eventsWPF DataGrid 列标题鼠标事件
【发布时间】:2020-06-25 10:08:21
【问题描述】:

我正在尝试通过将一些方法和属性添加到派生控件中来扩展标准 DataGrid 功能:

public class ExtendedGrid : DataGrid
{
    ...
}

但是,我仍然不清楚如何处理标题的鼠标事件。由于DataGridColumnHeader 不是可视化树的一部分(如果我错了请纠正我),将事件处理程序设置为MouseEvent 的唯一方法是在其上应用样式(How do I capture "Click" events on a DataGrid column headers)。

由于我正在扩展 DataGrid,因此我想在代码中保留实现,而不添加任何 XAML,在我看来,这在代码可读性方面是不一致的。因此,我编写了以下代码:

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;
    }
}

此代码在 AutoGeneratedColumns 上调用,但效果不佳(例如,根本没有)。即使这样,它也会对设置标题的样式施加限制。

有没有办法在我的ExtendedGrid 中处理DataGridColumnHeader 鼠标事件而无需任何XAML?还是 XAML 样式总比搜索变通办法更好(而且可能过于复杂)?

【问题讨论】:

  • 也许您正在寻找活动GridViewColumnHeader.Click
  • 不,我正在寻找 MouseDown 事件

标签: c# wpf


【解决方案1】:

DataGridColumnHeader 当然是可视化树的一部分。 DataGridColumn 及其后代不属于可视化树,因为它们用作实际渲染的 CellTemplateHeaderTemplate 的数据对象。

请注意,由于DataGridColumnHeader 扩展了ButtonBase,您还可以执行分配给DataGridColumnHeader.CommandICommand

如果您想为某个事件执行相同的事件处理程序,例如,Click 为每个 DataGridColumnHeader,例如,为了执行基于列的排序,那么您可以简单地为路由事件直接注册一个处理程序DataGrid:

<DataGrid DataGridColumnHeader.Click="SortColumn_OnClick" />

或者在使用Style 时设置DataGrid.ColumnHeaderStyle

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

如果您正在寻找一列或多列的特定 DataGridColumnHeader,则遍历可视化树以查找它们。
通过处理DataGrid.Loaded事件可以获取到某一列的DataGridColumnHeader

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;
}

【讨论】:

  • 就是这样。顺便说一句,DataGrid 是否在其代码中的某处以相同的方式初始化这些 DataGridColumnHeaders,因此可以使用类似于您的答案的方式扩展此方法?
  • DataGrid 包含一个DataGridColumnHeadersPresenter,它生成DataGridColumnHeader 元素作为其子元素。没有这样的方法可以覆盖,例如处理与此过程相关的事件。这是内功。一般来说,不需要直接访问DataGridColumnHeader 容器。要附加命令或事件处理程序,您可以使用样式或事件触发器,并使用模板自定义它们的外观。 I 样式或事件触发器不是一个选项,您必须自己挖掘 DataGridColumnHeader 元素。
  • 您可以创建一个附加行为并通过附加属性公开此DataGridColumnHeader 项目或扩展DataGrid 并添加相应的逻辑或创建扩展方法。这是使其可重用的唯一方法。请注意,必须加载 DataGrid 才能访问 DataGridColumnHeader 项目。
猜你喜欢
  • 2019-02-09
  • 2012-10-26
  • 2011-01-28
  • 2011-09-20
  • 2019-06-20
  • 2018-10-08
  • 2012-11-16
  • 2014-01-14
  • 1970-01-01
相关资源
最近更新 更多