WPF Datagrid - 行的编程选择似乎打破了多选(特别是shift-click多选)

Posted

技术标签:

【中文标题】WPF Datagrid - 行的编程选择似乎打破了多选(特别是shift-click多选)【英文标题】:WPF Datagrid -- programmatic selection of row seems to break multi-select (shift-click multiselect, specifically) 【发布时间】:2011-11-01 08:52:38 【问题描述】:

我有一个 WPF DataGrid 控件,其 SelectionUnit 为“FullRow”,SelectionMode 为“Extended”,我以编程方式选择其中的一个项目(通常是第一个项目)。选择有效,但由于某种原因,任何形式的程序选择似乎都会破坏 shift-select 多选功能。

如果我单击 DataGrid 中的另一个项目(因此我刚刚单击的项目是唯一选择的项目),则 shift-select 将起作用。如果我以编程方式选择了该项目,它似乎只会中断。此外,在任何一种情况下,control-click 都可以选择多个项目——似乎只有 shift-select 被破坏了。

我尝试了各种形式的以编程方式选择单个项目,从简单到 myGrid.SelectedIndex = 0,到使用 DataGrid 的 ItemContainerGenerator 来获取 DataGridRow 对象的实例并在其上设置 IsSelected = true,但没有有用。

重新迭代 -- 项目的编程选择有效,但它会破坏 shift-click 选择。

以前有人遇到过这种情况吗?我尝试将焦点设置在以编程方式选择的 DataGridRow 实例上,但似乎没有帮助?

【问题讨论】:

对我来说看起来像是控件中的错误。感觉像是与 SelectedItem 与 SelectedItems 有关,但以编程方式设置 SelectedItems 似乎不起作用。 (不小心添加了这个作为答案而不是先评论,不确定我的删除是否有效) 【参考方案1】:

记住焦点和键盘焦点是有区别的。当您在代码中选择项目时,请检查哪些控件具有键盘焦点/常规焦点。我猜数据网格会失去这个焦点,直到你用鼠标点击它,然后它重新获得使用 ctrl 函数所需的焦点。

我在 C++ 应用程序中托管的 WPF 用户控件中遇到了这个问题。

【讨论】:

我也尝试过以编程方式设置键盘焦点,但无济于事。有趣的是,我的场景和你的一样——在 C++ (MFC) 应用程序中托管 WPF 内容。【参考方案2】:

我成功地使用反射解决了这个问题:

var method = typeof(DataGrid).GetMethod("HandleSelectionForCellInput", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(MyDataGrid, new object[]  cellToSelect, false, false, false );

【讨论】:

【参考方案3】:

在@ezolotko 的 sn-p 的帮助下,我刚刚解决了完全相同的问题。 因为网格会动态生成行,所以我需要订阅 ItemContainerGenerator.StatusChanged 事件并找到代表该元素的行中的第一个单元格。

要找到我使用的单元格 DataGridHelper class 并将其全部包装在附加的行为中:

using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using Speedwell.WPF.Helpers;

namespace Speedwell.WPF.Behaviors

    public static class DataGridSingleRowSelected
    
        public static readonly DependencyProperty IsSelectionFixEnabledProperty = DependencyProperty.RegisterAttached
        (
            "IsSelectionFixEnabled",
            typeof(bool?),
            typeof(DataGridSingleRowSelected),
            new PropertyMetadata(null, IsSelectionFixEnabledChanged)
        );

        public static bool GetIsSelectionFixEnabled(DataGrid element)
        
            return (bool)element.GetValue(IsSelectionFixEnabledProperty);
        

        public static void SetIsSelectionFixEnabled(DataGrid element, bool value)
        
            element.SetValue(IsSelectionFixEnabledProperty, value);
        

        private static void IsSelectionFixEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        
            var dataGrid = sender as DataGrid;
            if(dataGrid != null)
            
                if(args.OldValue == null)
                
                    dataGrid.ItemContainerGenerator.StatusChanged += (s, e) => ContainerStatusChanged(dataGrid, ((ItemContainerGenerator)s));
                
            
        

        private static void ContainerStatusChanged(DataGrid dataGrid, ItemContainerGenerator generator)
        
            if(generator != null && generator.Status == GeneratorStatus.ContainersGenerated && dataGrid.SelectedItems.Count == 1)
            
                var row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(dataGrid.SelectedItems[0]);
                if(row != null)
                
                    var cell = dataGrid.GetCell(row, 0);
                    if(cell != null)
                    
                        SelectCellMethod.Invoke(dataGrid, new object[]  cell, false, false, false );
                    
                
            
        

        private static readonly MethodInfo SelectCellMethod = typeof(DataGrid).GetMethod("HandleSelectionForCellInput", BindingFlags.Instance | BindingFlags.NonPublic);
    

如果您可以看到正确的选择,只能在选择单个(1)行时应用,这正是我需要的,似乎它也需要什么@ jordan0day请求。

【讨论】:

【参考方案4】:

我为这个问题苦苦挣扎了好几天,并尝试了很多我在互联网上找到的东西。最后,我通过研究 DataGrid 的源代码找到了适合我的解决方案。 在 DataGrid 中,我注意到一个名为 _selectionAnchor 的成员变量,并猜测这一定是用户在网格中展开选择的起点。我的解决方案是将此成员设置为所选行的第一个单元格。如果在代码中选择了一行,则此修复可确保在展开选择时它从所选行开始。

请注意,我使用来自this issue 的代码来启用多选。然后,在文件 MainWindow.xaml.cs 中,我添加了以下代码:

private void ExampleDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)

    if (ExampleDataGrid.SelectedItems.Count > 0)
    
        ExampleDataGrid.ScrollIntoView(ExampleDataGrid.SelectedItems[0]);

        // Make sure that when the user starts to make an extended selection, it starts at this one
        foreach (var cellInfo in ExampleDataGrid.SelectedCells)
        
            if (cellInfo.Column.DisplayIndex == 0)
            
                var cell = GetDataGridCell(cellInfo);
                cell?.Focus();
                var field = typeof(DataGrid).GetField("_selectionAnchor", BindingFlags.NonPublic | BindingFlags.Instance);
                field?.SetValue(ExampleDataGrid, cellInfo);
                break;
            
        
    


public DataGridCell GetDataGridCell(DataGridCellInfo cellInfo)

    var cellContent = cellInfo.Column.GetCellContent(cellInfo.Item);
    if (cellContent != null)
    
        return (DataGridCell)cellContent.Parent;
    

    return null;

在 xaml 文件中:

<vm:CustomDataGrid x:Name="ExampleDataGrid" ItemsSource="Binding ImportItems"
    SelectedItemsList="Binding SelectedImportItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged"
    AutoGenerateColumns="False" SelectionMode="Extended" IsReadOnly="True" CanUserAddRows="False"
    SelectionChanged="ExampleDataGrid_SelectionChanged">

【讨论】:

感谢分享!

以上是关于WPF Datagrid - 行的编程选择似乎打破了多选(特别是shift-click多选)的主要内容,如果未能解决你的问题,请参考以下文章

选择行时设置文本WPF DataGrid行的颜色

在 DataGrid WPF 中获取选定的行项

以编程方式在 WPF DataGrid 中选择多行

WPF DataGrid怎么实现多行选中,不能使用CheckBox

wpf datagrid绑定了数据 如果选中多行中怎么获取选中行的某列的值

在wpf datagrid中,想要根据一个条件来改变datagrid行的背景颜色