如果这些类中的任何一个具有共同的基类,那么您会更容易,例如,您可以将一个DataTemplate
用于多个类。
但是,如果这些模型中的每一个确实不同并且没有足够的共同点,那么您将需要使用 a DataTemplateSelector
,尽管内置机制可能就足够了。
设置
这是我为重现类似情况而打下的一些基础。每个不同的类都继承自,List<object>
因此它具有包含任何类型的子级的内置方式,但我不依赖于选择数据模板的共性。
public class P1 : List<object> {
public P1() {}
public P1( IEnumerable<object> collection ) : base( collection ) {}
}
此外,我的根数据源是类型的List<object>
,因此它可以包含任何类型的对象。
的Window
构造函数:
public MainWindow() {
InitializeComponent();
this.DataContext = MyDataSource.GetData(); // in which I construct the tree of parents and children
}
解决方案
HierarchicalDataTemplate
首先为每种类型制作s。如果任何类型不包含子项,您当然会DataTemplate
为它们制作 s :
<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
ItemsSource="{Binding}">
<TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C1}"
ItemsSource="{Binding}">
<TextBlock>a C1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C2}"
ItemsSource="{Binding}">
<TextBlock>a C2 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:Q1}"
ItemsSource="{Binding}">
<TextBlock>a Q1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B1}"
ItemsSource="{Binding}">
<TextBlock>a B1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B2}"
ItemsSource="{Binding}">
<TextBlock>a B2 object</TextBlock>
</HierarchicalDataTemplate>
由于每个类都来自List<object>
,因此对象本身是子项的来源,而不是属性上的集合,所以我使用ItemsSource="{Binding}"
. 如果子项位于名为 的属性下的集合中Children
,那么它ItemsSource="{Binding Children}"
当然会是 。这仍然允许每个对象在不同的地方有它的孩子。
在这个例子中实现你的最简单的方法DataTemplateSelector
是什么都不做。因为我只在数据模板上指定了DataType
而不是 an x:Key
,即使集合是模糊的 ( List<object>
),WPF 仍然会检查底层类型以确定它是否是P1
// Q1
etc。并找到正确HierarchicalDataTemplate
的使用方法。我TreeView
只需要看起来像这样:
<TreeView ItemsSource"{Binding}" />
但是,假设为了向您展示 aDataTemplateSelector
您不能依赖它隐式匹配 by Type
。您将x:Key
s 放在您的数据模板上,例如:
<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
ItemsSource="{Binding}">
<TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
然后您的选择器可能会在内部使用 aDictionary
来确定用于给定的资源键Type
(请记住,这是一个幼稚的实现):
public class CustomDataTemplateSelector : DataTemplateSelector {
static Dictionary<Type, object> typeToKey = new Dictionary<Type, object>();
static CustomDataTemplateSelector() {
typeToKey[ typeof( P1 ) ] = "myKeyforP1";
}
public override DataTemplate SelectTemplate( object item, DependencyObject container ) {
var element = container as FrameworkElement;
if ( element != null && item != null ) {
var itemtype = item.GetType();
object keyObject;
if ( typeToKey.TryGetValue( itemtype, out keyObject ) ) {
var template = element.TryFindResource( keyObject ) as DataTemplate;
if ( template != null ) {
return template;
}
}
}
return base.SelectTemplate( item, container );
}
}
然后,您将选择器添加到资源字典中,并且您TreeView
需要分配另一个属性:
<Grid.Resources>
<loc:CustomDataTemplateSelector x:Key="mySelector" />
</Grid.Resources>
<TreeView ItemsSource="{Binding}"
ItemTemplateSelector="{StaticResource mySelector}"></TreeView>
并且因为base.SelectTemplate()
将尝试将项目Type
用作资源键,您可以拥有HierarchicalDataTemplate
模型的标准和仅在您使用自定义时TreeView
才会使用的自定义版本DataTemplateSelector
:
<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
ItemsSource="{Binding}">
<TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
ItemsSource="{Binding}">
<TextBlock>a P1 that looks much different</TextBlock>
</HierarchicalDataTemplate>