问题描述
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" });
这是结果...