WPF树形视图支持多选

11

请看这个讨论:https://dev59.com/F3RB5IYBdhLWcg3w77sn#9297158 - user1211839
请在此处检查我的答案: https://dev59.com/F3RB5IYBdhLWcg3w77sn#13412801 - Ignacio Soler Garcia
2个回答

14

以下代码运行正常且更简单。但缺点是使用了TreeView类的非公共属性IsSelectionChangeActive。代码如下:

private static readonly PropertyInfo IsSelectionChangeActiveProperty 
  = typeof (TreeView).GetProperty
    (
      "IsSelectionChangeActive",
      BindingFlags.NonPublic | BindingFlags.Instance
    );

public static void AllowMultiSelection(TreeView treeView)
{
  if (IsSelectionChangeActiveProperty==null) return;

  var selectedItems = new List<TreeViewItem>();
  treeView.SelectedItemChanged += (a, b) =>
  {
    var treeViewItem = treeView.SelectedItem as TreeViewItem;
    if (treeViewItem == null) return;

    // allow multiple selection
    // when control key is pressed
    if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
    {
      // suppress selection change notification
      // select all selected items
      // then restore selection change notifications
      var isSelectionChangeActive = 
        IsSelectionChangeActiveProperty.GetValue(treeView, null);

      IsSelectionChangeActiveProperty.SetValue(treeView, true, null);
      selectedItems.ForEach(item => item.IsSelected = true);

      IsSelectionChangeActiveProperty.SetValue
      (
        treeView, 
        isSelectionChangeActive, 
        null
      );
    }
    else
    {
      // deselect all selected items except the current one
      selectedItems.ForEach(item => item.IsSelected = (item == treeViewItem) );
      selectedItems.Clear();
    }

    if (!selectedItems.Contains(treeViewItem))
    {
      selectedItems.Add(treeViewItem);
    }
    else
    {
      // deselect if already selected
      treeViewItem.IsSelected = false;
      selectedItems.Remove(treeViewItem);
    }
  };

}

1
不错!这个方法可行,使用了现有的TreeView(即不从头开始重写),并使用了TreeView自己的IsSelected依赖属性。只需要将TreeViewItem更改为我设置绑定的项即可,其他都很好。谢谢。 - markmuetz
你最终解决了 Ctrl+选择和Ctrl+取消选择的错误吗? - user589195
嗨,@LonelyPixel,你修好了吗? - Christoffer Lette
1
我并没有使用这段代码。TreeViewEx开源项目(我稍作修改但尚未发布)为我提供了更好的基础。它基本上是TreeView控件的重写,已经内置了所需的功能。(并且源代码在您的控制下进行进一步的微调和修复。) - ygoe
1
我有点困惑,这个属性和方法应该在哪个类中定义? - Pratik
显示剩余3条评论

0

根据您需要的确切语义,解决方案可能非常简单:

如果您的树的根不是TreeView,例如如果它是一个普通的ItemsControl,则树中的所有TreeViewItem都可以独立选择,因此您基本上可以免费获得多选。 因此,只需使用ItemsControl而不是TreeView作为您的树的根。

这个解决方案的好处是实现起来非常简单。 它与mattdlong的解决方案不同之处在于:

  • 他的解决方案在点击项目时取消选择所有其他项目,因此您必须使用ctrl-click选择多个项目。
  • 通过此解决方案,单击将选择/取消选择您单击的项目,但是没有一种方法可以快速选择项目并同时取消选择所有其他项目。

另一个区别是他的解决方案中键盘导航(箭头键)会取消选择所有项目,而在此解决方案中键盘导航不会取消选择项目。

您应该根据您喜欢的语义(单击添加项目还是ctrl-click添加项目等)在这些解决方案之间进行选择。 如果您想要更高级的语义,例如Shift-Click等,那么相对容易添加。

请注意,您还可以使用 ToggleButtonCheckBoxItemContainerTemplate 中的任何位置自定义样式 TreeViewItems,并将其设置为 Checked={Binding IsSelected}。这允许用户通过单击 ToggleButtonCheckBox 来选择项目。

1
我在UI中有一些关于树形结构的经验,学到了一个规则 - 在正确实现多选时有很多细节需要考虑,我真的不想走这条路。我更喜欢找那些专门设计UI控件并已经投入测试和调整的人,而不是制作一些快速解决问题但需要不断投入资源来修复和维护的东西。当然,这对于编程练习来说也是不错的。 - mark
1
我觉得你误解了我的回答。我想说的是,TreeViewItem支持简单的多选功能,所以你根本不需要编写任何代码。如果你喜欢它内置的多选语义,你可以直接使用它。如果你想要与内置的多选行为不同的东西,那么你就必须要么购买一个控件,要么像mattdlong描述的那样编写代码。 - Ray Burns
3
当我尝试用ItemsControl替换TreeView控件时,出现了一大堆长达一页的错误追踪信息,似乎是由于某个TreeviewItem的样式不能被应用到这个控件上。那这该怎么办呢?它还能像分层树形控件一样工作吗?还是会变成一个平面列表(这样就没用了)? - ygoe
是的,提供一个工作示例会更好,伙计。我和LonelyPixel遇到了同样的问题... - Riegardt Steyn
使用VS 2015,我无法在TreeView中的ItemsControl中选择TreeViewItem子项。 - Andrew

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