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

WPF TreeView多TreeViewItemStyle绑定 动态生成单选/复选框实现咨询

解决方案

核心问题是你之前给TreeView硬编码指定了单一的ItemTemplate,所有节点都只会匹配单选框模板,只要改成基于数据类型的自动模板匹配即可适配多模型场景,具体修改步骤如下:


1. 重构视图模型层

先抽离公共属性做基础节点类,不同类型节点继承基类实现区分:

// 所有树节点的基类,实现INotifyPropertyChanged接口
public abstract class TreeNodeBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public string Name { get; set; }
    public ObservableCollection<TreeNodeBase> Children { get; set; } = new ObservableCollection<TreeNodeBase>();
    
    private bool? _isChecked;
    public bool? IsChecked
    {
        get => _isChecked;
        set { _isChecked = value; OnPropertyChanged(); }
    }
}

// 分类父节点:纯文本展示,无选择控件
public class CategoryNode : TreeNodeBase { }

// 复选框子节点
public class CheckBoxNode : TreeNodeBase { }

// 单选框子节点
public class RadioButtonNode : TreeNodeBase
{
    public string GroupName { get; set; }
}

主窗口ViewModel构造节点树,替代原来手动创建TreeViewItem的逻辑:

public class MainViewModel
{
    public ObservableCollection<TreeNodeBase> TreeItems { get; set; } = new ObservableCollection<TreeNodeBase>();

    public MainViewModel()
    {
        // 烘焙分类(复选框子项)
        var bakeCategory = new CategoryNode { Name = "Выпечка" };
        bakeCategory.Children.Add(new CheckBoxNode { Name = "торт Птичье молоко - 350 звезд" });
        bakeCategory.Children.Add(new CheckBoxNode { Name = "пироженное Тирамиссу - 25 UAH" });
        bakeCategory.Children.Add(new CheckBoxNode { Name = "пироженное Тирамиссу - 70 звезд" });
        bakeCategory.Children.Add(new CheckBoxNode { Name = "тарт Вишня - 35 звезд" });
        TreeItems.Add(bakeCategory);

        // 冰淇淋分类(单选框子项)
        var iceCategory = new CategoryNode { Name = "Мороженное" };
        iceCategory.Children.Add(new RadioButtonNode { Name = "ванильное - 15 звезд", GroupName = "grp1" });
        iceCategory.Children.Add(new RadioButtonNode { Name = "шоколадное - 15 звезд", GroupName = "grp1" });
        iceCategory.Children.Add(new RadioButtonNode { Name = "клубничное - 15 звезд", GroupName = "grp1" });
        TreeItems.Add(iceCategory);

        // 咖啡分类按同样逻辑添加即可
    }
}

窗口构造函数绑定上下文:

public MainWindow()
{
    InitializeComponent();
    DataContext = new MainViewModel();
}

2. 修改XAML配置

删除TreeView硬编码的ItemTemplate属性,将所有HierarchicalDataTemplate改为基于DataType的隐式模板,WPF会自动根据节点类型匹配对应模板:

<Window.Resources>
    <!-- 原有TreeView、TreeViewItem样式保留不变 -->
    <Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
        <Setter Property="IsExpanded" Value="True" />
        <Setter Property="IsSelected" Value="{Binding IsInitiallySelected, Mode=OneTime}" />
        <Setter Property="KeyboardNavigation.AcceptsReturn" Value="True" />
        <Setter Property="dw:VirtualToggleButton.IsVirtualToggleButton" Value="True" />
        <Setter Property="dw:VirtualToggleButton.IsChecked" Value="{Binding IsChecked}" />
        <Setter Property="FontSize" Value="30"/>
    </Style>
    <!-- 其余原有全局样式保留 -->

    <!-- 新增分类父节点模板 -->
    <HierarchicalDataTemplate DataType="{x:Type local:CategoryNode}" ItemsSource="{Binding Children, Mode=OneTime}">
        <TextBlock Text="{Binding Name}" />
    </HierarchicalDataTemplate>

    <!-- 复选框节点模板,去掉x:Key改为隐式模板 -->
    <HierarchicalDataTemplate DataType="{x:Type local:CheckBoxNode}" ItemsSource="{Binding Children, Mode=OneTime}" >
        <StackPanel Orientation="Horizontal">
            <CheckBox Focusable="False" IsChecked="{Binding IsChecked}" VerticalAlignment="Center" />
            <ContentPresenter Content="{Binding Name, Mode=OneTime}" Height="30" Margin="8,0" />
        </StackPanel>
    </HierarchicalDataTemplate>

    <!-- 单选框节点模板,去掉x:Key改为隐式模板 -->
    <HierarchicalDataTemplate DataType="{x:Type local:RadioButtonNode}" ItemsSource="{Binding Children, Mode=OneTime}">
        <StackPanel Orientation="Horizontal">
            <RadioButton Focusable="False" IsChecked="{Binding IsChecked}" GroupName="{Binding GroupName}" VerticalAlignment="Center" />
            <ContentPresenter Content="{Binding Name, Mode=OneTime}" Height="30" Margin="8,0" />
        </StackPanel>
    </HierarchicalDataTemplate>
</Window.Resources>

<DockPanel>
    <!-- TreeView去掉ItemTemplate属性,指定ItemsSource绑定TreeItems -->
    <TreeView x:Name="tree" Width="450" 
              ItemContainerStyle="{StaticResource TreeViewItemStyle}"  
              ItemsSource="{Binding TreeItems, Mode=OneTime}" />
    <!-- 右侧按钮、标签布局完全保留不变 -->
</DockPanel>

如果后续有更复杂的模板匹配逻辑,可以自行实现DataTemplateSelector,重写SelectTemplate方法判断节点类型返回对应模板,再将选择器赋值给TreeView的ItemTemplateSelector属性即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 05:36:05