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

WinUI TreeView单DataTemplate中放置多个TreeViewItem的实现问题

解决WinUI TreeView单个DataTemplate中嵌套多个可展开节点的问题

问题根源

你当前的写法存在两个核心问题:

  1. 在PersonTemplate的StackPanel中直接放置TreeViewItem时,这个TreeViewItem是作为普通UI控件存在,而非外部TreeView的逻辑节点。外部TreeView不会为它生成展开箭头,也不会将ItemTemplateSelector应用到它的子项上。
  2. TreeView的ItemTemplate是用来定义单个节点的内容,而非用来嵌套TreeViewItem控件,正确的层级节点展示需要依赖HierarchicalDataTemplate来关联子项数据源。

解决方案

通过调整数据结构+使用HierarchicalDataTemplate,实现Person节点展开后展示姓名、可展开的Books和Movies分组的需求:

1. 新增分组类与Movie类

先定义一个Group类来统一Books和Movies的分组逻辑,同时补充Movie类:

public class Group : ObservableObject
{
    [ObservableProperty]
    private string name;

    [ObservableProperty]
    private ObservableCollection<object> items;

    public Group(string name, ObservableCollection<object> items)
    {
        Name = name;
        Items = items;
    }
}

public partial class Movie : ObservableObject
{
    [ObservableProperty]
    private string director;

    [ObservableProperty]
    private string title;

    public Movie(string director, string title)
    {
        Director = director;
        Title = title;
    }
}

2. 修改Person类,添加分组集合

让Person类包含Books、Movies分组,作为TreeView的子节点数据源:

public partial class Person : ObservableObject
{
    [ObservableProperty]
    private string firstName;

    [ObservableProperty]
    private string lastName;

    public ObservableCollection<Book> Books { get; } = new();
    public ObservableCollection<Movie> Movies { get; } = new();

    // 用于TreeView的子节点分组
    public ObservableCollection<Group> Groups { get; } = new();

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
        // 初始化Books和Movies分组
        Groups.Add(new Group("Books", new ObservableCollection<object>(Books)));
        Groups.Add(new Group("Movies", new ObservableCollection<object>(Movies)));
    }
}

3. 更新XAML模板与选择器

使用HierarchicalDataTemplate定义各层级节点的模板,同时扩展模板选择器支持新的类型:

<Window
    x:Class="SimpleTreeViewExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SimpleTreeViewExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.Resources>
            <!-- Person节点模板:显示姓名,子节点为分组集合 -->
            <HierarchicalDataTemplate x:Key="PersonTemplate" x:DataType="local:Person"
                                      ItemsSource="{x:Bind Groups}">
                <StackPanel>
                    <TextBlock Text="{x:Bind FirstName}" />
                    <TextBlock Text="{x:Bind LastName}" />
                </StackPanel>
            </HierarchicalDataTemplate>

            <!-- 分组节点模板:显示分组名称,子节点为对应书籍/电影列表 -->
            <HierarchicalDataTemplate x:Key="GroupTemplate" x:DataType="local:Group"
                                      ItemsSource="{x:Bind Items}">
                <TextBlock Text="{x:Bind Name}" FontWeight="Bold"/>
            </HierarchicalDataTemplate>

            <!-- Book节点模板 -->
            <DataTemplate x:Key="BookTemplate" x:DataType="local:Book">
                <StackPanel>
                    <TextBlock Text="{x:Bind Writer}" />
                    <TextBlock Text="{x:Bind Title}" />
                </StackPanel>
            </DataTemplate>

            <!-- Movie节点模板 -->
            <DataTemplate x:Key="MovieTemplate" x:DataType="local:Movie">
                <StackPanel>
                    <TextBlock Text="{x:Bind Director}" />
                    <TextBlock Text="{x:Bind Title}" />
                </StackPanel>
            </DataTemplate>

            <!-- 扩展后的模板选择器 -->
            <local:TemplateSelector x:Key="TemplateSelector"
                                    PersonTemplate="{StaticResource PersonTemplate}"
                                    GroupTemplate="{StaticResource GroupTemplate}"
                                    BookTemplate="{StaticResource BookTemplate}"
                                    MovieTemplate="{StaticResource MovieTemplate}"/>
        </Grid.Resources>

        <TreeView x:Name="PackageReferenceTree"
                  ItemsSource="{x:Bind Persons}" 
                  ItemTemplateSelector="{StaticResource TemplateSelector}" />
    </Grid>
</Window>

4. 更新模板选择器代码

让选择器支持Person、Group、Book、Movie四种类型:

public class TemplateSelector : DataTemplateSelector
{
    public DataTemplate PersonTemplate { get; set; }
    public DataTemplate GroupTemplate { get; set; }
    public DataTemplate BookTemplate { get; set; }
    public DataTemplate MovieTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        return item switch
        {
            Person => PersonTemplate,
            Group => GroupTemplate,
            Book => BookTemplate,
            Movie => MovieTemplate,
            _ => throw new NotSupportedException($"不支持的类型: {item.GetType()}")
        };
    }
}

5. 测试数据示例

在MainWindow中添加Movie测试数据:

public sealed partial class MainWindow : Window
{
    public ObservableCollection<Person> Persons { get; } = new();
        
    public MainWindow()
    {
        this.InitializeComponent();
        Person person1 = new Person("John", "Doe");
        person1.Books.Add(new Book("Stephen King", "The Shining"));
        person1.Movies.Add(new Movie("Christopher Nolan", "Inception"));
        Persons.Add(person1);
    }
}

效果说明

  • 展开Person节点时,会显示姓名以及Books、Movies两个可展开的分组节点
  • 展开Books/Movies分组时,会显示对应的书籍/电影列表,且模板选择器会自动匹配对应的子项模板

内容的提问来源于stack exchange,提问作者JerryA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 18:01:30