Перетаскивание в TreeView, поиск индекса, куда вставить вставленный элемент - PullRequest
3 голосов
/ 16 сентября 2009

У меня есть WPF TreeView с одним уровнем дочерних элементов. Я использую HierarchicalDataTemplate для элементов верхнего уровня, поэтому дочерние элементы привязываются к списку резервных данных.

Когда я делаю перетаскивание, я хочу выяснить, куда в списке целей должны идти новые элементы. Я разбил сценарии на следующие случаи:

  1. Я нахожусь на пустой части цели TreeView
  2. Я нахожусь на одном из верхних уровней или рядом с ним TreeViewItem s (выпавший предмет должен идти в конец списка)
  3. Я парю над одним из дочерних предметов, и в этом случае упавший предмет должен идти вперед или назад от текущего предмета, в зависимости от того, висел ли я над верхней или нижней частью предмета.

У меня вопрос: откуда мне знать, на какой TreeViewItem я нахожусь? Как я узнаю, что это родительский или дочерний тип TreeViewItem? (У них разные DataContext типы данных) Должен ли я провести какое-то тестирование? Как узнать, какой элемент верхнего уровня владеет текущим дочерним элементом, над которым я нахожу курсор?

Ответы [ 3 ]

6 голосов
/ 16 сентября 2009

Попробовав кучу вещей, я думаю, что нашел способ сделать это. События DragOver и Drop отправляют вам параметр DragEventArgs. Вы используете это, чтобы сделать хит-тестирование. Если вы попали в цель, вы вряд ли попадете в нужный предмет. Скорее, вы столкнетесь с чем-то, что является частью шаблона элемента. Чтобы добраться до интересующих вас элементов TreeView, вы можете попробовать пройтись по визуальному дереву.

В этом примере 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 Вы можете также рассмотреть возможность изменения какого-либо значения в классах данных Bound (например, наличие свойства IsHovering, которое может выделить элемент посредством привязки данных)

3 голосов
/ 21 февраля 2013

Вот более простой и лаконичный способ, спасибо за указание, хотя 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;
}
1 голос
/ 06 июня 2018

Я думаю, что есть более простой способ определить падение TreeViewItem.

Внутри приватной пустоты Tree_Drop (отправитель объекта, DragEventArgs e) DragEventArgs e содержит «OriginalSource» типа данных «объект»; во время выполнения он приводится к типу данных TextBlock. Внутри 'OrginalSource' есть 'DataContext'; во время выполнения он преобразуется в объект, определенный в xaml для TreeViewItem. Таким образом, вы можете получить целевой TreeViewItem с помощью (e.OriginalSource как TextBlock) .DataContext как XXX.

...