【问题标题】:How to get item from item container in WPF?如何从 WPF 中的项目容器中获取项目?
【发布时间】:2020-01-15 02:29:19
【问题描述】:

我目前正在尝试在树视图中进行拖放操作(使用数据绑定和 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 为 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;
        }
    }

我已经花了好几天的时间来思考为什么这似乎不起作用,所以如果有人可以给我一些关于我做错了什么的指示或其他方式我可以从它的容器中获取该项目,这将是一个救命稻草。如果写得不好,也请原谅,我筋疲力尽,要睡觉了。

【问题讨论】:

  • 我不打算提供这个作为答案,因为它大约是 5 年前在 Xaml 变体 Silverlight 中。但是我在尝试在两个控件之间拖动时遇到了类似的障碍;我发现在放置时,没有办法辨别(对象的文本除外)究竟被删除了,就好像你在删除时得到了一个新的空白副本。 Uggg,我不记得确切的解决方案,或者我是否不得不放弃它......但你可能需要做更多的挖掘。

标签: c# wpf data-binding


【解决方案1】:

您的问题有点令人困惑。但看起来您正在尝试获取TreeViewItem 的数据项,这是拖放操作的放置目标。

这很简单。您只需要知道,如果项目容器是通过数据绑定 (ItemsControl.ItemsSource) 自动生成的,则容器的 DataContext 就是数据项目本身。
这适用于 ItemsControl 的所有物品容器(例如,ComboBoxItemListBoxItemListViewItem)。

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

备注

您似乎使用了ItemContainerGenerator 错误。 TreeView.ItemContainerGenerator 只会处理顶级项目(即子项目)。但是由于树节点可以有子节点,每个TreeViewItem 本身就是一个ItemsControl,因为它包含一个ItemsPresenter 来显示子项。
因此,您必须使用适当的ItemContainerGenerator 来检索子容器,否则ItemContainerGenerator 将返回null

对于顶级项目,请使用TreeView.ItemContainerGenerator
对于子项,请使用父项的 TreeViewItem.ItemContainerGenerator

此外,在启用 UI 虚拟化的情况下,加载 TreeView 时并非所有容器都生成。它们是在需要时生成的,例如用于显示。这些容器(TreeViewItem)也被共享以节省资源。因此,一旦您设置了 TreeViewItem.Tag 属性,它的值可能会丢失,因为稍后会生成一个新的 TreeViewItem 实例来包装数据项。

因此,您从根节点开始并获取其生成的容器。现在通过使用特定算法遍历TreeViewItems 执行树搜索,直到找到DataContext 等于您要查找的数据项的节点,例如,修改Tag 属性。
您访问例如的孩子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)
{
  ...
}

【讨论】:

    猜你喜欢
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 2023-03-05
    • 1970-01-01
    • 2017-11-18
    相关资源
    最近更新 更多