Wpf 拖放到树视图,查找要在其中插入拖放项的索引

Wpf 拖放到树视图,查找要在其中插入拖放项的索引,wpf,drag-and-drop,treeview,Wpf,Drag And Drop,Treeview,我有一个WPFTreeView,其中有一级子项。我对顶级项使用了hierarchycaldatatemplate,因此子项被绑定到支持数据列表 当我进行拖放操作时,我想找出新项目在目标列表中的位置。我将这些场景分解为以下几种情况: 我在目标的空部分TreeView 我在顶级treevieItems上或附近徘徊(已删除的项目必须放在列表的后面) 我将鼠标悬停在其中一个子项上,在这种情况下,被放下的项必须转到当前项的前面或后面,这取决于我是将鼠标悬停在项的上半部分还是下半部分 我的问题是,我如何知道

我有一个WPF
TreeView
,其中有一级子项。我对顶级项使用了
hierarchycaldatatemplate
,因此子项被绑定到支持数据列表

当我进行拖放操作时,我想找出新项目在目标列表中的位置。我将这些场景分解为以下几种情况:

  • 我在目标的空部分
    TreeView
  • 我在顶级
    treevieItem
    s上或附近徘徊(已删除的项目必须放在列表的后面)
  • 我将鼠标悬停在其中一个子项上,在这种情况下,被放下的项必须转到当前项的前面或后面,这取决于我是将鼠标悬停在项的上半部分还是下半部分

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

    在尝试了很多事情之后,我认为我找到了一种方法来实现这一点。
    DragOver
    Drop
    事件向您发送一个
    DragEventArgs
    参数。您可以使用它进行命中测试。如果你击中了目标,你不太可能直接击中你想要的目标。更确切地说,您将访问构成项目模板一部分的内容。要访问您感兴趣的TreeViewItems,您可以尝试沿着可视化树行走

    在本例中,顶级
    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");
        }
    

    如果你想给出一个视觉上的指示,该项目将下降,考虑使用一个装饰品喜欢。您也可以考虑在绑定数据类上改变某种类型的值(例如,具有<代码> iHOPENRIN < /COD>属性,可以通过数据绑定突出显示该项)

    < P>这是一个更简单更简洁的方式,谢谢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;
    }
    
    private void Tree\u DragOver(对象发送方,DragEventArgs e){
    TreeViewItem TreeViewItem=FindAncestor((DependencyObject)e.OriginalSource);
    if(treeViewItem!=null){
    treeViewItem.Background=画笔.Blue;
    }
    }   
    私有无效树(对象发送方,DragEventArgs e){
    TreeViewItem TreeViewItem=FindAncestor((DependencyObject)e.OriginalSource);
    if(treeViewItem!=null){
    treeViewItem.Background=画笔.白色;
    }
    }
    私有静态T FindAncestor(DependencyObject当前),其中T:DependencyObject{//在VisualTree中搜索指定的类型
    while(当前!=null){
    如果(电流为T){
    返回(T)电流;
    }
    当前=VisualTreeHelper.GetParent(当前);
    }
    返回null;
    }
    
    我认为有一种更简单的方法来确定drop TreeViewItem

    private void Tree\u Drop(object sender,DragEventArgs e)
    中,参数
    DragEventArgs e
    包含数据类型
    对象的字段
    OriginalSource

    在运行时,它被强制转换为数据类型
    TextBlock
    。在
    OrginalSource
    内部,有一个
    DataContext
    ;在运行时,这将强制转换到在xaml中为
    TreeViewItem
    定义的对象。因此,您可以通过
    (例如,OriginalSource作为TextBlock)、DataContext作为XXX来获取目标
    TreeViewItem