为包含多个集合作为子项的 TreeView 节点构建数据模板的正确方法是什么?

问题描述

我有一个包含多个属性集合的类:

class Foo{
   public ObservableCollection<Bar> Bars {get; set;}
   public ObservableCollection<Baz> Bazzes {get; set;}
}

我试图在 TreeView显示它,其中 Foo 节点位于根节点,然后在它下面是 Bars 集合的节点,其中包含每个Bar 元素作为子节点,Bazzes 集合也是如此。但我似乎无法正确获取数据模板。我设法得到的最接近的是这样的:

                <HierarchicalDataTemplate DataType="{x:Type local:Foo}">
                    <TreeViewItem Header="Root">
                        <TreeViewItem Header="Bars" ItemsSource="{Binding Path=Bars}"/>
                        <TreeViewItem Header="Bazzes" ItemsSource="{Binding Path=Bazzes}"/>
                    </TreeViewItem>
                </HierarchicalDataTemplate>
                <DataTemplate DataType="{x:Type local:Bar}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" />
                        <TextBlock Text=" (" Foreground="Blue" />
                        <TextBlock Text="{Binding Type}" Foreground="Blue" />
                        <TextBlock Text=")" Foreground="Blue" />
                    </StackPanel>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:Baz}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" />
                    </StackPanel>
                </DataTemplate>

这将显示一个带有节点的分层树,我可以通过单击小三角形打开以显示子项目,但是当我尝试单击任何项​​目时,它会选择整个 Foo 其子项作为一大选择。我假设这是因为包含集合的节点已集成到 Foo 的模板中,因此它以某种方式将它们视为一个大节点?但是我不知道如何让集合显示为子节点而不这样做。

我正在寻找的设置类型的正确方法是什么,因为这显然不太正确?

解决方法

您的实施存在一些基本问题。第一个是 Tree 只是一个映射到绑定数据结构上,它应该是一棵树。首先,我们必须让你的 Foo 类成为一棵树......

public class BarBazBase
{
    public string Name { get; set; }
    public string Type { get; set; }

}
public class Bar : BarBazBase
{
    public string BarSpecial { get; set; }
}
public class Baz : BarBazBase
{
    public string BazSpecial { get; set; }
}


public class Foo : ObservableCollection<ObservableCollection<BarBazBase>>
{
    public ObservableCollection<BarBazBase> Bars { get; set; } = new ObservableCollection<BarBazBase>();
    public ObservableCollection<BarBazBase> Bazzes { get; set; } = new ObservableCollection<BarBazBase>();
    public Foo()
    {
        Add(Bars);
        Add(Bazzes);
    }
}

接下来我们需要为每种类型的树节点使用不同的模板。因此我们需要一个数据模板选择器

public class BasBazTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item,DependencyObject container)
    {
        FrameworkElement fe = container as FrameworkElement;
        if(item is Foo)
        {
            return fe.FindResource("TreeHeader") as DataTemplate;
        }
        if (item is ObservableCollection<BarBazBase> baseCollection)
        {
            if (baseCollection.Count > 0 && baseCollection[0] is Bar)
                return fe.FindResource("BarHeader") as DataTemplate;
            else if (baseCollection.Count > 0 && baseCollection[0] is Baz)
                return fe.FindResource("BazHeader") as DataTemplate;
            else
                return null;
        }
        else if (item is Bar)
        {
            return fe.FindResource("BarItemTemplate") as DataTemplate;
        }
        else if (item is Baz)
        {
            return fe.FindResource("BazItemTemplate") as DataTemplate;
        }
        else
        {
            return null;
        }
    }
}

最后,我们已准备好将所有内容整合到 XAML 中...

       <TreeView x:Name="treeView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Beige"
              ItemsSource="{Binding Root}" ItemTemplateSelector="{StaticResource BasBazTemplateSelector}">
        <TreeView.Resources>
            <HierarchicalDataTemplate x:Key="TreeHeader" ItemsSource="{Binding}" >
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate x:Key="BazHeader" ItemsSource="{Binding}">
                <Label>Baz</Label>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate x:Key="BarHeader" ItemsSource="{Binding}">
                <Label>Bar</Label>
            </HierarchicalDataTemplate>
            <DataTemplate x:Key="BarItemTemplate">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}" />
                    <TextBlock Text=" (" Foreground="Blue" />
                    <TextBlock Text="{Binding Type}" Foreground="Blue" />
                    <TextBlock Text=")" Foreground="Blue" />
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="BazItemTemplate">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}" />
                </StackPanel>
            </DataTemplate>
        </TreeView.Resources>
    </TreeView>

一些松散的结局...

<Window.Resources>
    <local:BasBazTemplateSelector x:Key="BasBazTemplateSelector"/>
</Window.Resources>

public Foo Root { get; set; }

        Root = new Foo();
        Root.Bars.Add(new Bar() { Name = "a",Type = "a0",BarSpecial = "a bar" });
        Root.Bars.Add(new Bar() { Name = "b",Type = "b0",BarSpecial = "another bar" });
        Root.Bazzes.Add(new Baz() { Name = "c",Type = "c0",BazSpecial = "a baz" });
        Root.Bazzes.Add(new Baz() { Name = "d",Type = "d0",BazSpecial = "another baz" });

这是结果...

enter image description here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...