HierarchicalDataTemplate TreeView - ContainerFromItem只返回第一个I

本文关键字:返回 第一个 ContainerFromItem TreeView HierarchicalDataTemplate | 更新日期: 2023-09-27 18:06:43

我有一个如下的TreeView:

<TreeView
    Loaded="tv_Loaded_1"
    DockPanel.Dock="Bottom"
    Name="tv"
    ItemsSource="{Binding XPath=*}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate
            ItemsSource="{Binding XPath=*}">
            <StackPanel
                Orientation="Horizontal">
                <TextBlock
                    Text="{Binding Name}"></TextBlock>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

在代码后面,我使用以下xml作为DataContext:

XmlDocument doc = new XmlDocument();
doc.LoadXml(@"<a><b><c></c><d></d></b><e><f></f><g></g></e></a>");
DataContext = doc;

TreeView正在生成良好,但在枚举项目时,我得到TreeViewItem仅用于第一个XmlNode(根节点即<a>)和剩余的XmlNodes在层次结构中没有任何相应的TreeViewItem存在。

private IEnumerable<TreeViewItem> Enumerate(ItemCollection items)
{
    foreach (XmlElement element in items)
    {
        TreeViewItem item = tv.ItemContainerGenerator.ContainerFromItem(element) as TreeViewItem;
        if (item != null) //When second call with <a>.Items, item is null
        {
            yield return item;
        }
        //Enumerate is called again with <a>.Items 
        //Exception in second call, because item is null
        foreach (TreeViewItem i in Enumerate(item.Items))
        {
            yield return i;
        }
    }
}
private void tv_Loaded_1(object sender, RoutedEventArgs e)
{
   var list = Enumerate(tv.Items).ToList();
}

为什么在树的XmlNodes的其余部分没有任何TreeViewItem存在?

HierarchicalDataTemplate TreeView - ContainerFromItem只返回第一个I

这是因为其他项目不在tv中,它们在'a' TreeViewItem中。但是,他们真的还没有在'a',因为TreeViewItem没有展开,布局还没有更新。

你可以通过传入ItemsControl (tv或父TreeViewItem)并使用它来获取ContainerFromItem来使其工作。但是,在你得到一个容器之前,你必须展开项目并更新它的布局。

下面是完成上述任务的一些代码。副作用是树被完全展开。
private IEnumerable<TreeViewItem> Enumerate(ItemsControl itemsControl, ItemCollection items)
{
    foreach (XmlElement element in items)
    {
        TreeViewItem item = itemsControl.ItemContainerGenerator.ContainerFromItem(element) as TreeViewItem;
        if (item != null) //When second call with <a>.Items, item is null
        {
            item.IsExpanded = true;
            item.UpdateLayout();
            yield return item;
        }
        //Enumerate is called again with <a>.Items 
        //Exception in second call, because item is null
        foreach (TreeViewItem i in Enumerate(item, item.Items))
        {
            yield return i;
        }
    }
}
private void tv_Loaded_1(object sender, RoutedEventArgs e)
{
    var list = Enumerate(tv, tv.Items).ToList();
}

实际上,你只需要传入ItemsControl。此外,我还加入了一些代码来关闭树。

private IEnumerable<TreeViewItem> Enumerate(ItemsControl itemsControl)
{
    foreach (XmlElement element in itemsControl.Items)
    {
        TreeViewItem item = itemsControl.ItemContainerGenerator.ContainerFromItem(element) as TreeViewItem;
        if (item != null) //When second call with <a>.Items, item is null
        {
            item.IsExpanded = true;
            item.UpdateLayout();
            yield return item;
        }
        //Enumerate is called again with <a>.Items 
        //Exception in second call, because item is null
        foreach (TreeViewItem i in Enumerate(item))
        {
            yield return i;
        }
    }
}
private void tv_Loaded_1(object sender, RoutedEventArgs e)
{
    var list = Enumerate(tv).ToList();
    // Unexpand all the items
    list.ForEach(tvi => tvi.IsExpanded = false);
}