我想将多个不同的列表绑定到WPF中的TreeView。我查找了其他解决方案,但找不到解决我的问题的任何帮助。 This answer距离我很近,但与我要寻找的不完全相同。
我尝试了上面的链接解决方案,但它在TreeView中仅显示两个级别。 我不知道如何在TreeView中将每个列表的名称显示为父级。
我要显示的对象如下:
public class LightDistributor
{
public string Description { get; set; }
// ...
public List<Field> Hardware { get; set; }
public List<Type> Inputs { get; set; }
public List<Type> Outputs { get; set; }
}
public class Field
{
public string Fieldname { get; set; }
// ...
}
public class Type
{
public string TypeDescription { get; set; }
// ...
}
还有XAML:
<TreeView ItemsSource="{Binding LightDistributors}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type data:LightDistributor}" ItemsSource="{Binding Hardware}">
<TextBlock Text="{Binding Description}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type data:Field}">
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
我希望我的Treeview看起来是什么:
LightDistributor - LongFloor
| Hardware
- Field1
- Field2
- Field3
| Inputs
- InputTypeA
- InputTypeB
| Outputs
- OutputTypeY
- OutputTypeZ
当前外观:
LightDistributor - LongFloor
- Field1
- Field2
- Field3
取决于SelectedItem,显示带有更多参数的UserControl。
答案 0 :(得分:1)
添加一个NamedSection
,该名称将名称与项目列表分组:
public class NamedSection
{
public string Name { get; set; }
public IReadOnlyList<object> Items { get; set; }
}
然后更新您的LightDistributor
。请注意,我是如何使List<T>
属性仅具有getter的功能,以便NamedSection
可以正确地捕获构造参考。
public class LightDistributor
{
public string Description { get; set; }
// ...
public List<Field> Hardware { get; } = new List<Field>();
public List<Type> Inputs { get; } = new List<Type>();
public List<Type> Outputs { get; } = new List<Type>();
public List<NamedSection> Sections { get; }
public LightDistributor()
{
this.Sections = new List<NamedSection>()
{
new NamedSection() { Name = "Hardware", Items = this.Hardware },
new NamedSection() { Name = "Inputs", Items = this.Inputs },
new NamedSection() { Name = "Outputs", Items = this.Outputs },
};
}
}
然后输入您的XAML:
<TreeView ItemsSource="{Binding LightDistributors}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:LightDistributor}" ItemsSource="{Binding Sections}">
<TextBlock Text="{Binding Description}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:NamedSection}" ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:Field}">
<TextBlock Text="{Binding Fieldname}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Type}">
<TextBlock Text="{Binding TypeDescription}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
我最初认为您也可以通过将x:Array
中的TreeViewItem
声明为资源(每个项分别用于“硬件”,“输入”,“输出”),然后将其设置为HierarchicalTemplate的ItemsSource来实现适用于LightDistributor。但这是行不通的,因为似乎没有一种方法可以为我们要显示的每个LightDistributor克隆此x:Array
。
答案 1 :(得分:1)
在此处添加另一个答案,以显示如何在纯XAML中执行此操作。这几乎完全基于canton7最初的第二个解决方案,该解决方案非常接近,但是创建了一个TreeViewItem数组,这些数组正在回收。通常设置x:Shared="False"
应该可以解决此问题,在我看来,它不起作用实际上对我来说是WPF的错误。
无论如何,不是创建控件数组,而是创建数据对象数组。在这种情况下,类型sys:String可以正常工作,并具有额外的好处,我们稍后也可以将其用作TreeViewItem标头文本:
<x:Array x:Key="DistributorItems" Type="{x:Type sys:String}">
<sys:String>Hardware</sys:String>
<sys:String>Inputs</sys:String>
<sys:String>Outputs</sys:String>
</x:Array>
这些代表LightDistributor类中的子属性。 TreeView的第二级将获得分配为其DataContext的以下字符串之一,因此我们将为其创建样式,并使用触发器通过父级TreeViewItem的DataContext相应地设置ItemsSource
:
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Style.Triggers>
<Trigger Property="DataContext" Value="Hardware">
<Setter Property="ItemsSource" Value="{Binding DataContext.Hardware, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=1}}" />
</Trigger>
<Trigger Property="DataContext" Value="Inputs">
<Setter Property="ItemsSource" Value="{Binding DataContext.Inputs, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=1}}" />
</Trigger>
<Trigger Property="DataContext" Value="Outputs">
<Setter Property="ItemsSource" Value="{Binding DataContext.Outputs, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=1}}" />
</Trigger>
</Style.Triggers>
</Style>
除了我将LightDistributor的ItemContainerStyle
设置为我上面创建的样式以相应地设置ItemsSource之外,其余代码与canton7的原始代码基本相同。
<TreeView ItemsSource="{Binding LightDistributors}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:LightDistributor}"
ItemsSource="{Binding Source={StaticResource DistributorItems}}"
ItemContainerStyle="{StaticResource TreeViewItemStyle}">
<TextBlock Text="{Binding Description}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type vm:Field}">
<TextBlock Text="{Binding Fieldname}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Type}">
<TextBlock Text="{Binding TypeDescription}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
现在,仅仅因为这行之有效并不意味着它是一个好的解决方案,但我仍然认为canton7的第一个解决方案是更好的解决方案。只是将其扔在那里,以表明它毕竟可以在纯XAML中完成。