简体   繁体   English

如何从WPF中的项目容器中获取项目?

[英]How to get item from item container in WPF?

Im currently trying to get drag/drop within a treeview working(using databinding and HierarchicalDataTemplate), and have the dragging working, but im running into a problem when trying to get the drop working, since i need to get the data item being dropped on and add it to its item collection of children nodes.我目前正在尝试在树视图中进行拖放操作(使用数据绑定和 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
        }
    }

My best idea on how to get around this was to assign the treeitem as the tag of the treeviewitem on initialization/loading of the tree, and to do so i need to get the itemcontainer of the item, which i already have a working function to get that which i use when initiating the DoDrag()关于如何解决这个问题,我最好的想法是在树的初始化/加载时将 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;
        }
    }

This seems to work perfectly when using to start the DoDrag()这在用于启动 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");
        }
    }

But when i try to use it in my function to assign container tags it says that Container was null and that GetParentContainerFromItem returned null, yet my function does not log that it returned null但是当我尝试在我的函数中使用它来分配容器标签时,它说 Container 为 null 并且 GetParentContainerFromItem 返回 null,但我的函数没有记录它返回 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;
        }
    }

Ive spent days stumped on why this does not seem to work, so if someone could give me some pointers on what im doing wrong or another way i could get the item from its container it would be a lifesaver.我花了几天的时间苦苦思索为什么这似乎不起作用,所以如果有人能给我一些关于我做错了什么的指示,或者我可以从容器中取出物品的其他方式,那将是一个救星。 Also please excuse if this is poorly written, i am exhausted and about to go to sleep.如果写的不好,还请见谅,我已经筋疲力尽了,准备睡觉了。

Your question is a little bit confusing.你的问题有点令人困惑。 But it looks like you are trying to get the data item of the TreeViewItem which is the drop target of the drag&drop operation.但看起来您正在尝试获取作为拖放操作的放置目标的TreeViewItem的数据项。

This is pretty simple.这很简单。 All you need to know is that if the item container is auto-generated via data binding ( ItemsControl.ItemsSource ) the DataContext of the container is the data item itself.您需要知道的是,如果项容器是通过数据绑定 ( ItemsControl.ItemsSource ) 自动生成的,则容器的DataContext就是数据项本身。
This applies to all item containers of an ItemsControl (eg, ComboBoxItem , ListBoxItem , ListViewItem ).这适用于ItemsControl所有项目容器(例如, ComboBoxItemListBoxItemListViewItem )。

So TreeViewItem.DataContext references the underlying TreeItem instance that is wrapped by the TreeViewItem :所以TreeViewItem.DataContext引用由TreeViewItem包装的底层TreeItem实例:

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;
  }
}

Remark评论

It looks like you are using the ItemContainerGenerator wrong.看起来您使用ItemContainerGenerator错误的。 TreeView.ItemContainerGenerator will only handle top level items (ie child items). TreeView.ItemContainerGenerator将只处理顶级项目(即子项目)。 But as a tree node can have child nodes, each TreeViewItem is itself an ItemsControl as it contains an ItemsPresenter to display child items.但是由于树节点可以有子节点,每个TreeViewItem本身就是一个ItemsControl因为它包含一个ItemsPresenter来显示子项。
Therefore you have to use the appropriate ItemContainerGenerator to retrieve the child container or ItemContainerGenerator will return null .因此,您必须使用适当的ItemContainerGenerator来检索子容器,否则ItemContainerGenerator将返回null

For top-level items use TreeView.ItemContainerGenerator .对于顶级项目,请使用TreeView.ItemContainerGenerator
For child items use the parent's TreeViewItem.ItemContainerGenerator .对于子项,使用父项的TreeViewItem.ItemContainerGenerator

Also, in case of UI virtualization is enabled not all containers are generated when the TreeView is loaded.此外,在启用 UI 虚拟化的情况下,并非所有容器都会在加载TreeView时生成。 They are generated when need eg, for display.它们在需要时生成,例如用于显示。 Those containers (the TreeViewItem ) are also shared to save resources.这些容器( TreeViewItem )也被共享以节省资源。 So once you set the TreeViewItem.Tag property its value might get lost as a new TreeViewItem instance is generated later to wrap the data item.因此,一旦您设置了TreeViewItem.Tag属性,它的值可能会丢失,因为稍后会生成一个新的TreeViewItem实例来包装数据项。

So you start at the root node and get its generated container.因此,您从根节点开始并获取其生成的容器。 Now perform a tree search by traversing the TreeViewItems using a specific algorithm until you found the node where the DataContext equals the data item you are looking for and eg, modify the Tag property.现在通过使用特定算法遍历TreeViewItems来执行树搜索,直到找到DataContext等于您正在查找的数据项的节点,例如,修改Tag属性。
You access the children of eg treeViewItemA by referencing the treeViewItemA.Items property and get their containers by calling treeVieItemA.ItemContainerGenerator.ContainerFromItem method for each child:您可以通过引用treeViewItemA.Items属性访问例如treeViewItemA的子项,并通过为每个子项调用treeVieItemA.ItemContainerGenerator.ContainerFromItem方法来获取它们的容器:

Example例子

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;
  }
}

Usage用法

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM