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

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

问题描述

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



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

$ System.Windows.Controls.DataGrid.OnSelectionChanged(SelectionChangedEventArgs e)中的b
$ b

 在System.Windows.Controls.Primitives.Selector.SelectionChanger中为
。 End()
在System.Windows.Controls.DataGrid.MakeFullRowSelection(ItemInfo信息,布尔值allowExtendSelect,布尔允许MinimalSelect)
在System.Windows.Controls.DataGrid.HandleSelectionForCellInput(DataGridCell单元格,布尔值startDragging,布尔值AllowExtendSelect ,在System.Windows.Controls.DataGridCell.OnAnyMouseLeftButtonDown(MouseButtonEventArgs e)处为Boolean allowMinimalSelect)
,在System.Windows处为System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler,Object target)处的
。 RoutedEventHandlerInfo.InvokeHandler(Object target,RoutedEventArgs routedEventArgs)
位于System.Windows.EventRoute.InvokeHandlersImpl(Object so urce,RoutedEventArgs args,布尔值重新升高)
在System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender,RoutedEventArgs args,RoutedEvent newEvent)
在System.Windows.UIElement.OnMouseDownThunk(Object sender,MouseButtonEventArgs e)$ System.Windows.RoutedEventArgs.InvokeHandler(委托处理程序,对象目标)上的b
$ b

因此看起来像我应该能够通过重写DataGridCell.OnMouseLeftButtonDown来修改行为,就像这样:

  class MultiDragDataGridCell:DataGridCell 
{
受保护的重写void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
//这允许用户通过在
之前处理事件来单击并拖动多重选择默认行为(取消选择除单击的单元格之外的所有内容)。
如果(IsSelected& Keyboard.Modifiers == ModifierKeys.None)
{
e.Handled = true;
}

base.OnMouseLeftButtonDown(e);
}
}

但是,我很难将DataGrid转换为创建一个MultiDragDataGridCell而不是普通的DataGridCell,因为实例化DataGridCell的类是内部的。任何人都知道我如何实现这一目标,或者是否还有另一种方法可以实现我想要的行为?



我尝试过的其他方法:




  • 设置DataGridCell的样式以向MouseLeftButtonDown添加处理程序。这是行不通的,因为它是在选择更改后执行的。

  • 设置DataGridCell的样式,以便将处理程序添加到PreviewMouseLeftButtonDown。可行,但是它阻止我单击单元格内的任何按钮等。


解决方案

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


但是,由于实例化DataGridCell的类是内部的,我无法让DataGrid创建MultiDragDataGridCell而不是普通的DataGridCell。 。任何人都知道我该如何实现。.


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



请参考以下示例代码:



扩展DataGrid控件

 公共类ExtendedDataGrid:DataGrid 
{
受保护的重写DependencyObject GetContainerForItemOverride()
{
/ /这为DataGrid提供了DataGridRow的自定义版本
return new ExtendedDataGridRow();
}
}

公共类ExtendedDataGridRow:DataGridRow {}

公共类ExtendedDataGridCellsPresenter:System.Windows.Controls.Primitives.DataGridCellsPresenter
{
保护的重写DependencyObject GetContainerForItemOverride()
{
//这为DataGrid提供了DataGridCell的自定义版本
return new ExtendedDataGridCell();
}
}

公共类ExtendedDataGridCell:DataGridCell
{
//您的自定义/重写实现可在此处添加
}

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



另外,请注意,扩展 DataGrid DataGridRow 来提供自定义的 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天全站免登陆