【问题标题】:Dragging and dropping to a TreeView, finding the index where to insert the dropped item拖放到一个TreeView,找到插入拖放项的索引
【发布时间】:2010-11-28 15:14:25
【问题描述】:

我有一个 WPF TreeView 与一级子项。我将HierarchicalDataTemplate 用于顶级项目,因此子项目绑定到支持数据列表。

当我进行拖放时,我想找出新项目应该在目标列表中的哪个位置。我将场景分为以下几种情况:

  1. 我在目标的空白部分TreeView
  2. 我悬停在顶级TreeViewItems 之一上或附近(删除的项目必须放在列表的后面)
  3. 我悬停在其中一个子项目上,在这种情况下,被丢弃的项目必须位于当前项目的前面或后面,具体取决于我是悬停在项目的上半部分还是下半部分。

我的问题是,我怎么知道我悬停在哪个 TreeViewItem 上?我如何知道它是父类型还是子类型TreeViewItem? (他们有不同的DataContext 数据类型)我应该做一些命中测试吗?我如何知道哪个顶级项目拥有我悬停的当前子项目?

【问题讨论】:

    标签: wpf drag-and-drop treeview


    【解决方案1】:

    我认为有一个更简单的方法来确定drop TreeViewItem。

    private void Tree_Drop(object sender, DragEventArgs e)内部,参数DragEventArgs e包含数据类型为object的字段OriginalSource

    在运行时,它被转换为数据类型TextBlock。在OrginalSource里面,有一个DataContext;在运行时,这被强制转换为在 xaml 中为TreeViewItem 定义的对象。所以你可以通过(e.OriginalSource as TextBlock).DataContext as XXX得到目标TreeViewItem

    【讨论】:

      【解决方案2】:

      这是一个更简单更简洁的方法,感谢 Pieter Breed 的指导。

      private void Tree_DragOver(object sender, DragEventArgs e) {
      TreeViewItem treeViewItem = FindAncestor<TreeViewItem>((DependencyObject)e.OriginalSource);
      if (treeViewItem != null) {
          treeViewItem.Background = Brushes.Blue;
      }
      }   
      private void Tree_DragLeave(object sender, DragEventArgs e) {
      TreeViewItem treeViewItem = FindAncestor<TreeViewItem>((DependencyObject)e.OriginalSource);
      if (treeViewItem != null) {
          treeViewItem.Background = Brushes.White;
      }
      }
      private static T FindAncestor<T>(DependencyObject current) where T : DependencyObject { // Search the VisualTree for specified type
      while (current != null) {
          if (current is T) {
              return (T)current;
          }
          current = VisualTreeHelper.GetParent(current);
      }
      return null;
      }
      

      【讨论】:

        【解决方案3】:

        在尝试了很多事情之后,我想我想出了一个方法来做到这一点。 DragOverDrop 事件向您发送 DragEventArgs 参数。你用它来做命中测试。但是,如果您进行了测试,则不太可能直接击中您想要的项目。相反,您将遇到构成项目模板一部分的内容。要找到您感兴趣的 TreeViewItems,您可以尝试沿着 Visual Tree 向上走。

        在此示例中,顶级 TreeViewItems 绑定到 GroupItem 实例,子节点绑定到 DragItems 实例。 tv 是 TreeView 元素本身的名称。在这段代码中,假设我会找到它是安全的,因为事件处理程序是在这个元素上定义的。

        我创建了以下沿着树向上走的代码。

            private void FindDropTarget(
                out TreeViewItem pGroupingNode, 
                out TreeViewItem pItemNode,
                DragEventArgs pDragEventArgs)
            {
                pItemNode = null;
                pGroupingNode = null;
        
                DependencyObject k = VisualTreeHelper.HitTest(tv, pDragEventArgs.GetPosition(tv)).VisualHit;
        
                while (k != null)
                {
                    if (k is TreeViewItem)
                    {
                        TreeViewItem treeNode = k as TreeViewItem;
                        if (treeNode.DataContext is GroupItem)
                        {
                            pGroupingNode = treeNode;
                        }
                        else if (treeNode.DataContext is DragItems)
                        {
                            pItemNode = treeNode;
                        }
                    } 
                    else if (k == tv)
                    {
                        Console.WriteLine("Found treeview instance");
                        return;
                    }
        
                    k = VisualTreeHelper.GetParent(k);
                }
            }
        

        像这样消费它。请注意检查IsVisible,这很重要:

            private void tv_DragOver(object sender, DragEventArgs e)
            {
                TreeViewItem groupingNode, itemNode;
                FindDropTarget(out groupingNode, out itemNode, e);
        
                GroupItem groupingData = (groupingNode != null ? groupingNode.DataContext as GroupItem : null);
                DragItems dragItem = (itemNode != null && itemNode.IsVisible ? itemNode.DataContext as DragItems : null);
        
                Console.WriteLine("Hovering ...");
                Console.WriteLine(
                    "Grouping Node = {0}, Item Node = {1}",
                    groupingData != null ? groupingData.Title : "null",
                    dragItem != null ? dragItem.Id : "null");
            }
        

        如果您想对物品掉落的位置提供某种视觉指示,请考虑使用像 Bea Stollnitz explains here 这样的装饰器。您还可以考虑更改 Bound 数据类上的某种值(例如具有可以通过数据绑定突出显示项目的 IsHovering 属性)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多