You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 15:42:22