拖放到TreeView,找到插入已删除项目的索引

时间:2009-09-15 22:33:40

标签: wpf drag-and-drop treeview

我有一个含有一级子项的WPF TreeView。我使用HierarchicalDataTemplate作为顶级项目,因此子项目绑定到后备数据列表。

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

  1. 我在目标TreeView
  2. 的空白部分
  3. 我在其中一个顶级TreeViewItem上面或附近徘徊(已删除的项目必须位于列表的后面)
  4. 我在其中一个子项目上徘徊,在这种情况下,被删除的项目必须转到当前项目的前面或后面,这取决于我是否悬停在项目的上半部分或下半部分。
  5. 我的问题是,我如何知道我正在徘徊的TreeViewItem?我怎么知道它是父类型还是子类型TreeViewItem? (他们有不同的DataContext数据类型)我应该进行某种类型的测试吗?我如何知道哪个顶级项目拥有我正在悬停的当前子项?

3 个答案:

答案 0 :(得分:6)

在尝试了很多事情后,我想我已经找到了一种方法来做到这一点。 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属性,该属性可以通过数据绑定突出显示该项)

答案 1 :(得分:3)

这是一种更简洁的方式,感谢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;
}

答案 2 :(得分:1)

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

在private void Tree_Drop(对象发送者,DragEventArgs e)中,DragEventArgs e包含数据类型为'object'的'OriginalSource';在运行时,它转换为数据类型TextBlock。在'OrginalSource'里面,有'DataContext';在运行时,它转换为在xaml中为TreeViewItem定义的对象。所以你可以通过(e.OriginalSource as TextBlock).DataContext获取目标TreeViewItem为XXX。