WPF数据网格 -- 编程选择行似乎会破坏多选(特别是shift-click多选)

5
我有一个WPF DataGrid控件,SelectionUnit设置为"FullRow",SelectionMode设置为"Extended",我正在程序化地选择其中的一个项目(通常是第一个项目)。选择有效,但由于某种原因,任何形式的程序化选择似乎都会破坏shift-select多选功能。
如果我单击DataGrid中的另一个项目(所以我刚刚单击的项目是唯一选定的项目),那么shift-select将起作用。只有在我通过编程方式选择该项目时,它才似乎会中断。此外,在任何情况下,控制单击都可以选择多个项目--似乎只是shift-select不起作用。
我尝试了各种形式的程序化选择单个项目,从简单的myGrid.SelectedIndex = 0,到使用DataGrid的ItemContainerGenerator获取DataGridRow对象的实例并在其上设置IsSelected = true,但没有成功。
再次强调--程序化选择项目有效,但它会破坏shift-click选择。
有人遇到过这种情况吗?我已经尝试将焦点设置在程序化选择的DataGridRow实例上,但似乎没有帮助?

对我来说,这看起来像是控件中的一个错误。感觉好像与SelectedItem vs. SelectedItems有关,但以编程方式设置SelectedItems似乎不起作用。(我先不小心将其添加为答案而不是评论,不确定我的删除是否成功) - Dana Cartwright
4个回答

3
我使用反射成功解决了这个问题:
var method = typeof(DataGrid).GetMethod("HandleSelectionForCellInput", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(MyDataGrid, new object[] { cellToSelect, false, false, false });

2
我曾经为这个问题苦苦挣扎了多日,试过很多在网上找到的方法。最终,通过研究DataGrid的源代码,我找到了适合我的解决方案。 在DataGrid中,我注意到一个名为_selectionAnchor的成员变量,并猜测这必须是用户在网格中扩展选择时的起点。我的解决方案是将该成员设置为所选行的第一个单元格。如果在代码中选择了一行,则此修复程序确保在扩展选择时从所选行开始。
请注意,我使用了此问题中的代码来启用多选。然后,在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">

1

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

我在一个WPF用户控件中遇到了这个问题,我们将其托管在一个C++应用程序中。


我已经尝试了编程方式来设置键盘焦点,但是没有成功。有趣的是,我的情况与你的一样——在C++(MFC)应用程序中托管WPF内容。 - Jordan0Day

0
我刚刚通过 @ezolotko 的代码片段解决了完全相同的问题。 由于网格是动态生成行,我需要订阅 ItemContainerGenerator.StatusChanged 事件,并找到表示此元素的行中的第一个单元格。
为了找到单元格,我使用了 DataGridHelper 类 并将其全部包装在附加行为中:
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所请求的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接