Как получить товар из контейнера товаров в WPF? - PullRequest
0 голосов
/ 15 января 2020

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

//treeitem is the name of my item data class

private void TreeViewItem_Drop(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent("DragableTreeViewItem"))
        {
     //the data of the treeitem i need to duplicate, provided by the drag operation
            TreeItem data = e.Data.GetData("DragableTreeViewItem") as TreeItem;
            TreeViewItem tvi = sender as TreeViewItem;
            //here im getting the treeviewitem, but i need the treeitem
        }
    }

Моя лучшая идея о том, как обойти это, состояла в том, чтобы назначить treeitem как тег treeviewitem при инициализации / загрузке дерева и для этого мне нужно получить itemcontainer элемента, который у меня уже есть рабочая функция, чтобы получить то, что я использую при запуске DoDrag ()

//returns the item container of the parent of the TreeItem given
private TreeViewItem GetParentContainerFromItem(TreeItem ti)
    {
        List<TreeItem> GetOrderedParents(TreeItem item)
        {
            TreeItem currentParent = item;
            List<TreeItem> items = new List<TreeItem>();

            int i = 0;
            do
            {
                if (currentParent.parentItem != null)
                {
                    items.Insert(0, currentParent);
                    currentParent = currentParent.parentItem;
                }
                else
                {
                    items.Insert(0, currentParent);
                    i++;
                    return items;
                }
            } while (i == 0);
            return null;
        }

        //the local tree in a list, ordered from the original item (in this case "ti") at 0, down to the root at the end of the list
        List<TreeItem> LocalHierarchy = GetOrderedParents(ti);
        if (LocalHierarchy != null)
        {
         //print out the names of each treeitem in it, in order from the root down
            string hierarchyString = "";
            foreach (TreeItem t in LocalHierarchy)
            {
                if (hierarchyString == "")
                {
                    hierarchyString = t.Title;
                }
                else
                {
                    hierarchyString = (hierarchyString + ", " + t.Title);
                }
            }
            System.Console.WriteLine(hierarchyString);

            TreeViewItem localCurrentParent = null;
            TreeViewItem finalContainer = null;

            //walk down the tree in order to get the container of the parent 
            foreach (TreeItem t in LocalHierarchy)
            {

                //if the parent of the item given is a root node, meaning we can return its container
                if (LocalHierarchy.IndexOf(t) == 0 && (LocalHierarchy.IndexOf(t) == (LocalHierarchy.Count - 2)))
                {
                    finalContainer = treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
                    break;
                }
                else
                //if we're at a root node
                if (LocalHierarchy.IndexOf(t) == 0)
                {
                    localCurrentParent = treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
                }
                else
                //if we're at the 2nd to last, AKA the parent of the item given
                if (LocalHierarchy.IndexOf(t) == (LocalHierarchy.Count - 2))
                {
                    finalContainer = localCurrentParent.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
                    break;
                }
                else
                {
                    localCurrentParent = localCurrentParent.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
                }
            }

            if (finalContainer == null)
            {
                System.Console.WriteLine("Final container is null");
            }
            return finalContainer;
        }
        else
        {
            System.Console.WriteLine("ERROR: LocalHierarchy is null");
            return null;
        }
    }

Это, кажется, отлично работает при использовании чтобы запустить DoDrag ()

private void DoDrag()
    {
        if(selectedItem != null)
        {
            TreeItem t = selectedItem;
            TreeViewItem tvi = null;
            if (t.parentItem == null)
            {
                //it has no parent, and is a root node
                tvi = treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
            }
            else
            {
                //it has a parent, and i can get the container for the parent
                tvi = GetParentContainerFromItem(t).ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
            }
            DragDrop.DoDragDrop(tvi, new DataObject("DragableTreeViewItem", t, true), DragDropEffects.Copy);
            dragNeeded = false;
        }
        else if(selectedItem == null)
        {
            Console.WriteLine("Selected item was null; cant drag");
        }
    }

Но когда я пытаюсь использовать его в своей функции для назначения тегов контейнера, он говорит, что Container был нулевым и что GetParentContainerFromItem возвратил нулевое значение, однако моя функция не регистрирует, что вернула null

private void AssignContainerTag(TreeItem t)
    {
        TreeViewItem Container = null;

        if (t.parentItem == null)
        {
            //its a root node
            Container =  treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
            Container.Tag = t;
        }
        else
        {
            //it is not a root node
             Container = GetParentContainerFromItem(t).ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
            Container.Tag = t;
        }
    }

Я потратил несколько дней на то, почему это не так k, так что, если бы кто-то мог дать мне несколько советов о том, что я делаю неправильно, или как-то иначе я мог бы достать предмет из его контейнера, это было бы спасением. Также прошу прощения, если это плохо написано, я измотан и собираюсь go спать.

1 Ответ

0 голосов
/ 15 января 2020

Ваш вопрос немного сбивает с толку. Но похоже, что вы пытаетесь получить элемент данных TreeViewItem, который является целью перетаскивания операции перетаскивания.

Это довольно просто. Все, что вам нужно знать, это то, что если контейнер элемента генерируется автоматически с помощью привязки данных (ItemsControl.ItemsSource), DataContext контейнера является самим элементом данных.
Это относится ко всем контейнерам элементов ItemsControl (например, ComboBoxItem, ListBoxItem, ListViewItem).

Итак, TreeViewItem.DataContext ссылается на базовый TreeItem экземпляр, который обернут TreeViewItem:

private void TreeViewItem_Drop(object sender, DragEventArgs e)
{
  if (e.Data.GetDataPresent("DragableTreeViewItem"))
  {
    var sourceItem = e.Data.GetData("DragableTreeViewItem") as TreeItem;

    var dropTargetItemContainer = sender as TreeViewItem;
    var dropTargetItem = targetItemContainer.DataContext as TreeItem;
  }
}

Замечание

Похоже, вы используете ItemContainerGenerator неправильно. TreeView.ItemContainerGenerator будет обрабатывать только элементы верхнего уровня (то есть дочерние элементы). Но так как у узла дерева могут быть дочерние узлы, каждый TreeViewItem сам по себе является ItemsControl, поскольку он содержит ItemsPresenter для отображения дочерних элементов.
Поэтому вы должны использовать соответствующий ItemContainerGenerator для извлечения дочернего контейнера или ItemContainerGenerator вернет null.

Для элементов верхнего уровня используйте TreeView.ItemContainerGenerator.
Для дочерних элементов используйте родительский TreeViewItem.ItemContainerGenerator.

Кроме того, в случае Виртуализация пользовательского интерфейса включена, не все контейнеры генерируются при загрузке TreeView. Они генерируются при необходимости, например, для отображения. Эти контейнеры (TreeViewItem) также используются совместно для экономии ресурсов. Поэтому, как только вы установите свойство TreeViewItem.Tag, его значение может быть потеряно, так как новый экземпляр TreeViewItem будет сгенерирован позже для переноса элемента данных.

Итак, вы начинаете с узла root и получаете его сгенерированный контейнер. , Теперь выполните поиск по дереву, пройдя по TreeViewItems с использованием заданного алгоритма c, пока не найдете узел, где DataContext равен элементу данных, который вы ищете, и, например, измените свойство Tag.
You получить доступ к дочерним элементам, например, treeViewItemA, ссылаясь на свойство treeViewItemA.Items, и получить их контейнеры, вызвав метод treeVieItemA.ItemContainerGenerator.ContainerFromItem для каждого дочернего элемента:

Пример

public static class MyExtensions
{
  // Get item container of item from TreeView, TreeViewItem, ListView or any ItemsControl
  public static bool TryGetContainerOfChildItem<TItemContainer>(this ItemsControl itemsControl, object item, out TItemContainer itemContainer) where TItemContainer : DependencyObject
  {
    itemContainer = null;
    foreach (object childItem in itemsControl.Items)
    {
      if (childItem == item)
      {
        itemContainer = (TItemContainer) itemsControl.ItemContainerGenerator.ContainerFromItem(item);
        return true;
      }

      DependencyObject childItemContainer = itemsControl.ItemContainerGenerator.ContainerFromItem(childItem);
      if (childItemContainer is ItemsControl childItemsControl && childItemsControl.TryGetContainerOfChildItem(item, out itemContainer))
      {
        return true;
      }
    }

    return false;
  }
}

Использование

// Search whole TreeView
if (treeView.TryGetContainerOfChildItem(item, out TreeViewItem itemContainer)
{
  ...
}

// Search from a specific parent TreeViewItem node
if (treeViewItem.TryGetContainerOfChildItem(item, out TreeViewItem itemContainer)
{
  ...
}
...