HierarchicalDataTemplate TreeView - ContainerFromItem仅为第一个Item返回TreeViewItem

时间:2016-09-13 11:49:48

标签: c# wpf treeview

我有一个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生成正常,但在枚举Items时,我只为第一个XmlNode(根节点,即<a>)获取TreeViewItem,并且层次结构中的其余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();
}

为什么Tree中的其余XmlNode没有任何TreeViewItem?

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:3)

这是因为其他项目不在电视中,而是在&#39; a&#39;树型视图。但是,他们真的不在&#39; a&#39;但要么是因为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);
}