WPF TreeView重复定义同类型HierarchicalDataTemplate报键已存在错误
错误产生原因
- 第一个错误*"The key is already defined in this scope."*:WPF的资源字典(
ResourceDictionary)中,仅设置DataType不设置x:Key的隐式数据模板,会自动将DataType对应的值作为资源的唯一键。你为local:Apoteka类型定义了两个相同键的HierarchicalDataTemplate,违反了资源字典键唯一的规则,因此触发报错。 - 第二个错误*"All objects added to an IDictionary must have a Key attribute or some other type of key associated with them."*:
TreeView.Resources本质是实现了IDictionary接口的资源字典,内部存储的所有资源必须有唯一键。你删除第二个模板的DataType属性后,该模板既没有通过DataType自动生成的键,也没有手动设置x:Key,不符合字典的存储要求,因此触发报错。 - 额外的逻辑错误:单个
HierarchicalDataTemplate的ItemsSource只能绑定一个集合,就算绕开资源键的报错,也无法通过两个同类型模板让同一个节点同时加载两个子集合,原实现思路本身不符合WPF TreeView的模板规则。
正确实现方案
核心逻辑:单个类型节点只能对应一个层级模板,要在Apoteka节点下展示两个集合的内容,需要先将两个集合合并为统一的子项源,再为不同类型的子项分别定义对应的数据模板即可。
方案1:XAML端直接合并集合(无需修改后端类代码)
使用CompositeCollection在模板内直接合并两个子集合,示例代码如下:
<TreeView Grid.Column="0" Grid.Row="0" Name="trv1"> <TreeView.Resources> <!-- 为Apoteka定义唯一的层级模板 --> <HierarchicalDataTemplate DataType="{x:Type local:Apoteka}"> <HierarchicalDataTemplate.ItemsSource> <CompositeCollection> <CollectionContainer Collection="{Binding KategorijeLekovi, RelativeSource={RelativeSource AncestorType=TreeViewItem}}"/> <CollectionContainer Collection="{Binding KategorijeRadnici, RelativeSource={RelativeSource AncestorType=TreeViewItem}}"/> </CompositeCollection> </HierarchicalDataTemplate.ItemsSource> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Naziv}"/> </StackPanel> </HierarchicalDataTemplate> <!-- 分别为两个集合的元素类型定义对应模板,示例假设类型为KategorijaLek、KategorijaRadnik,根据实际类型替换即可 --> <HierarchicalDataTemplate DataType="{x:Type local:KategorijaLek}" ItemsSource="{Binding 对应子项属性}"> <TextBlock Text="{Binding 显示文本属性}"/> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="{x:Type local:KategorijaRadnik}" ItemsSource="{Binding 对应子项属性}"> <TextBlock Text="{Binding 显示文本属性}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView>
方案2:后端类新增合并属性(绑定更稳定)
如果不想写相对源绑定,可以直接在Apoteka类中新增一个只读属性,统一返回合并后的集合,示例代码如下:
public class Apoteka : INotifyPropertyChanged { public string Naziv { get; set; } public ObservableCollection<KategorijaLek> KategorijeLekovi { get; set; } = new(); public ObservableCollection<KategorijaRadnik> KategorijeRadnici { get; set; } = new(); public IList MergedChildren { get { var merged = new CompositeCollection(); merged.Add(new CollectionContainer { Collection = KategorijeLekovi }); merged.Add(new CollectionContainer { Collection = KategorijeRadnici }); return merged; } } // 其他业务逻辑、PropertyChanged触发逻辑省略 }
对应修改Apoteka的层级模板即可:
<HierarchicalDataTemplate DataType="{x:Type local:Apoteka}" ItemsSource="{Binding MergedChildren}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Naziv}"/> </StackPanel> </HierarchicalDataTemplate>
扩展场景
如果需要在两个集合外增加分组节点(比如先显示“药品分类”节点承载KategorijeLekovi,显示“员工分类”节点承载KategorijeRadnici),只需要构造两个对应的分组中间对象,将两个集合分别作为分组对象的子项,再把两个分组对象加入Apoteka的子项集合即可,不需要修改模板逻辑。
内容的提问来源于stack exchange,提问作者DataType
相关产品推荐
相关产品推荐

