繁体   English   中英

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

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

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

我花了几天的时间苦苦思索为什么这似乎不起作用,所以如果有人能给我一些关于我做错了什么的指示,或者我可以从容器中取出物品的其他方式,那将是一个救星。 如果写的不好,还请见谅,我已经筋疲力尽了,准备睡觉了。

你的问题有点令人困惑。 但看起来您正在尝试获取作为拖放操作的放置目标的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.Items属性访问例如treeViewItemA的子项,并通过为每个子项调用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)
{
  ...
}

暂无
暂无

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

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