如何覆盖 DataGrid 选择行为? [英] How can I override DataGrid selection behavior?

查看:23
本文介绍了如何覆盖 DataGrid 选择行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想通过以下方式修改 DataGrid 的选择行为.通常,当您选择了多行,然后单击已选择的项目之一时,选择将重置为仅单击的项目.我想更改它,以便如果在没有任何键盘修饰符的情况下单击多选行之一,则不会修改选择.这样做的目的是允许多项目拖放.

我注意到当上述默认行为被激活时,调用堆栈包括:

at System.Windows.Controls.DataGrid.OnSelectionChanged(SelectionChangedEventArgs e)在 System.Windows.Controls.Primitives.Selector.SelectionChanger.End()在 System.Windows.Controls.DataGrid.MakeFullRowSelection(ItemInfo info, Boolean allowedExtendSelect, Boolean allowedMinimalSelect)在 System.Windows.Controls.DataGrid.HandleSelectionForCellInput(DataGridCell cell, Boolean startDragging, Boolean allowedExtendSelect, Boolean allowedMinimalSelect)在 System.Windows.Controls.DataGridCell.OnAnyMouseLeftButtonDown(MouseButtonEventArgs e)在 System.Windows.RoutedEventArgs.InvokeHandler(委托处理程序,对象目标)在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(对象目标,RoutedEventArgs routedEventArgs)在 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)在 System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)在 System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e)在 System.Windows.RoutedEventArgs.InvokeHandler(委托处理程序,对象目标)

因此看起来我应该能够通过覆盖 DataGridCell.OnMouseLeftButtonDown 来修改行为,如下所示:

class MultiDragDataGridCell : DataGridCell{protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e){//这允许用户通过处理之前的事件来单击并拖动多选//默认行为(取消选择除了点击的单元格之外的所有内容)开始.if (IsSelected && Keyboard.Modifiers == ModifierKeys.None){e.handled = true;}base.OnMouseLeftButtonDown(e);}}

但是,我无法让 DataGrid 创建 MultiDragDataGridCell 而不是普通的 DataGridCell,因为实例化 DataGridCell 的类是内部的.任何人都知道我如何实现这一目标,或者是否有另一种方式来实现我想要的行为?

我尝试过的其他事情:

  • 设置 DataGridCell 样式以将处理程序添加到 MouseLeftButtonDown.这不起作用,因为它在选择已更改后执行.
  • 设置 DataGridCell 样式以将处理程序添加到 PreviewMouseLeftButtonDown.这有效,但它阻止我单击单元格内的任何按钮等.

解决方案

注意: 这个答案只是试图为问题中提到的以下问题提供解决方案;不是如何覆盖网格的选择行为.我希望一旦您有了一个自定义的 DataGridCell,它就可以成为您尝试做的事情的一个很好的起点.

<块引用>

但是,我无法让 DataGrid 创建 MultiDragDataGridCell 而不是普通的 DataGridCell,因为实例化 DataGridCell 的类是内部的.任何人都知道我是如何做到这一点的..

解决方案:为了确保 DataGrid 使用您自定义的 DataGridCell - 您需要重新模板化您的 DataGridRow 使用 DataGridCellsPresenter 的扩展版本,它反过来将提供您的自定义 DataGridCell.

请参考以下示例代码:

扩展 DataGrid 控件

public class ExtendedDataGrid : DataGrid{受保护的覆盖 DependencyObject GetContainerForItemOverride(){//这为DataGrid提供了DataGridRow的自定义版本返回新的 ExtendedDataGridRow();}}公共类 ExtendedDataGridRow : DataGridRow { }公共类 ExtendedDataGridCellsPresenter : System.Windows.Controls.Primitives.DataGridCellsPresenter{受保护的覆盖 DependencyObject GetContainerForItemOverride(){//这为DataGrid提供了一个自定义版本的DataGridCell返回新的 ExtendedDataGridCell();}}公共类 ExtendedDataGridCell : DataGridCell{//您可以在此处添加自定义/覆盖的实现}

在 XAML 中重新模板 DataGridRow(更全面的

另外,请注意,扩展 DataGridDataGridRow 以提供自定义 DataGridCell 不是强制性的 - 您可以实现相同的只需扩展 DataGridCellsPresenter(并且更新 DataGridRow 的控制模板以使用扩展版本)

I would like to modify the selection behavior of the DataGrid in the following way. Normally when you have multiple rows selected, and then you click one of the items already selected, the selection is reset to only the clicked item. I would like to change it such that if one of the multi-selected rows is clicked without any keyboard modifiers, the selection is not modified. The goal of this is to allow a multi-item drag-drop.

I noticed that when aforementioned default behavior is activated, the call stack includes:

at System.Windows.Controls.DataGrid.OnSelectionChanged(SelectionChangedEventArgs e)
at System.Windows.Controls.Primitives.Selector.SelectionChanger.End()
at System.Windows.Controls.DataGrid.MakeFullRowSelection(ItemInfo info, Boolean allowsExtendSelect, Boolean allowsMinimalSelect)
at System.Windows.Controls.DataGrid.HandleSelectionForCellInput(DataGridCell cell, Boolean startDragging, Boolean allowsExtendSelect, Boolean allowsMinimalSelect)
at System.Windows.Controls.DataGridCell.OnAnyMouseLeftButtonDown(MouseButtonEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
at System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)

Therefore it looks like I should be able to modify the behavior by overriding DataGridCell.OnMouseLeftButtonDown, something like this:

class MultiDragDataGridCell : DataGridCell
{
    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        // This allows users to click-and-drag a multi-selection by handling the event before
        // the default behavior (deselecting everything but the clicked cell) kicks in.
        if (IsSelected && Keyboard.Modifiers == ModifierKeys.None)
        {
            e.Handled = true;
        }

        base.OnMouseLeftButtonDown(e);
    }
}

However, I'm having trouble getting the DataGrid to create a MultiDragDataGridCell instead of a normal DataGridCell, since the class that instantiates DataGridCell is internal. Anyone know how I can achieve that, or if there's another way of achieving the behavior I want?

Other things I tried:

  • Styling the DataGridCell to add a handler to MouseLeftButtonDown. This doesn't work because it executes after the selection has already changed.
  • Styling the DataGridCell to add a handler to PreviewMouseLeftButtonDown. This works, but it prevents me from clicking any buttons, etc. inside the cell.

解决方案

Note: This answer only tries to provide a solution to following issue mentioned in the question; not how to override the grid's selection behavior. I am hoping that once you have a custom DataGridCell in place, it can be a good starting point for what you are trying to do.

However, I'm having trouble getting the DataGrid to create a MultiDragDataGridCell instead of a normal DataGridCell, since the class that instantiates DataGridCell is internal. Anyone know how I can achieve that..

Solution: In order to ensure that the DataGrid uses your custom DataGridCell - you need to re-template your DataGridRow to use an extended version of DataGridCellsPresenter which in-turn will provide your custom DataGridCell.

Please refer following sample code:

Extending DataGrid controls

public class ExtendedDataGrid : DataGrid
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        //This provides the DataGrid with a customized version for DataGridRow
        return new ExtendedDataGridRow();
    }
}

public class ExtendedDataGridRow : DataGridRow {  }

public class ExtendedDataGridCellsPresenter : System.Windows.Controls.Primitives.DataGridCellsPresenter
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        //This provides the DataGrid with a customized version for DataGridCell
        return new ExtendedDataGridCell();
    }
}

public class ExtendedDataGridCell : DataGridCell
{
    // Your custom/overridden implementation can be added here
}

Re-template DataGridRow in XAML (a more comprehensive template can be found at this link - I am only using a watered-down version of it for sake of readability).

 <Style TargetType="{x:Type local:ExtendedDataGridRow}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ExtendedDataGridRow}">
                    <Border x:Name="DGR_Border"
                          BorderBrush="{TemplateBinding BorderBrush}"
                          BorderThickness="{TemplateBinding BorderThickness}"
                          SnapsToDevicePixels="True">
                        <SelectiveScrollingGrid>
                            <SelectiveScrollingGrid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </SelectiveScrollingGrid.ColumnDefinitions>
                            <SelectiveScrollingGrid.RowDefinitions>
                                <RowDefinition Height="*" />
                                <RowDefinition Height="Auto" />
                            </SelectiveScrollingGrid.RowDefinitions>

              <!-- Make sure to register your custom DataGridCellsPresenter here as following --> 

                            <local:ExtendedDataGridCellsPresenter Grid.Column="1"
                                  ItemsPanel="{TemplateBinding ItemsPanel}"
                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            <DataGridDetailsPresenter Grid.Column="1"
                                    Grid.Row="1"
                                    Visibility="{TemplateBinding DetailsVisibility}"
                                    SelectiveScrollingGrid.SelectiveScrollingOrientation=
                                      "{Binding AreRowDetailsFrozen, 
                                      ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical},
                                      Converter={x:Static DataGrid.RowDetailsScrollingConverter}, 
                                      RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
                            <DataGridRowHeader Grid.RowSpan="2"
                                 SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
                                 Visibility="{Binding HeadersVisibility, 
                                  ConverterParameter={x:Static DataGridHeadersVisibility.Row}, 
                                  Converter={x:Static DataGrid.HeadersVisibilityConverter}, 
                                  RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />       
                        </SelectiveScrollingGrid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

And, your extended DataGrid's visual tree have the custom datagrid-cells:

Also, please note, it is not mandatory to extend DataGrid, or DataGridRow to provide a custom DataGridCell - you can achieve the same result by just extending DataGridCellsPresenter (and, updating DataGridRow's control-template to use the extended version)

这篇关于如何覆盖 DataGrid 选择行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆