Fork me on Github

带有复选框的 Controls.TreeView

 

1.定义 TreeView 模型

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ViewTreeDemo
{
    public class TreeViewItemModel : INotifyPropertyChanged
    {
        private bool _isChecked;
        private bool _isUpdatingChildren;

        public string Name { get; set; }

        public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                if (_isChecked != value)
                {
                    _isChecked = value;
                    OnPropertyChanged(nameof(IsChecked));

                    if (!_isUpdatingChildren && Children != null)
                    {
                        _isUpdatingChildren = true;

                        // 递归设置子节点的勾选状态
                        foreach (var child in Children)
                        {
                            child.IsChecked = value;
                        }

                        _isUpdatingChildren = false;
                    }

                    // 更新父节点的勾选状态
                    if (Parent != null)
                    {
                        UpdateParentCheckedState();
                    }
                }
            }
        }

        public ObservableCollection<TreeViewItemModel> Children { get; set; }

        public TreeViewItemModel Parent { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public void UpdateParentCheckedState()
        {
            if (Parent != null)
            {
                bool allSiblingsChecked = Parent.Children.All(child => child.IsChecked);
                bool anySiblingChecked = Parent.Children.Any(child => child.IsChecked);

                if (allSiblingsChecked)
                {
                    Parent._isChecked = true;
                }
                else if (anySiblingChecked)
                {
                    Parent._isChecked = false;
                }
                else
                {
                    Parent._isChecked = false;
                }

                Parent.OnPropertyChanged(nameof(IsChecked));
                Parent.UpdateParentCheckedState();
            }
        }
    }

}

 

2.在 ViewModel 中初始化 TreeView

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ViewTreeDemo
{
    public class MainWindowViewModel
    {
        public ObservableCollection<TreeViewItemModel> TreeDataCollection { get; set; }

        public MainWindowViewModel()
        {
            TreeDataCollection = new ObservableCollection<TreeViewItemModel>();

            // 添加根节点
            var rootNode = new TreeViewItemModel { Name = "所有权限", IsChecked = false, Children = new ObservableCollection<TreeViewItemModel>() };
            TreeDataCollection.Add(rootNode);

            // 添加子节点
            var childNode1 = new TreeViewItemModel { Name = "报表管理", IsChecked = false, Children = new ObservableCollection<TreeViewItemModel>(), Parent = rootNode };
            var childNode2 = new TreeViewItemModel { Name = "系统设置", IsChecked = false, Children = new ObservableCollection<TreeViewItemModel>(), Parent = rootNode };
            rootNode.Children.Add(childNode1);
            rootNode.Children.Add(childNode2);

            // 添加孙子节点
            var grandChildNode1 = new TreeViewItemModel { Name = "角色管理", IsChecked = false, Children = new ObservableCollection<TreeViewItemModel>(), Parent = childNode1 };
            var grandChildNode2 = new TreeViewItemModel { Name = "角色管理", IsChecked = false, Children = new ObservableCollection<TreeViewItemModel>(), Parent = childNode1 };
            var grandChildNode3 = new TreeViewItemModel { Name = "参数设置", IsChecked = false, Children = new ObservableCollection<TreeViewItemModel>(), Parent = childNode2 };
            childNode1.Children.Add(grandChildNode1);
            childNode1.Children.Add(grandChildNode2);
            childNode2.Children.Add(grandChildNode3);

        }
    }
}

 

3.绘制 View

<Window
    x:Class="ViewTreeDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:ViewTreeDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Window.Resources>
        <Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
            <Setter Property="Focusable" Value="False" />
            <Setter Property="Width" Value="16" />
            <Setter Property="Height" Value="16" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Border
                      HorizontalAlignment="Center"
                      VerticalAlignment="Center"
                      Width="26"
                      Height="26"
                      Background="Transparent">
                            <TextBlock
                          x:Name="ExpandPath"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          FontSize="22"
                          Text="▸" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="ExpandPath" Property="Text" Value="▾" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        <Style x:Key="Style_Default_TreeViewItem" TargetType="TreeViewItem">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Padding" Value="0,5,0,0" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TreeViewItem">
                        <StackPanel>
                            <Border
                          x:Name="Bd"
                          Padding="{TemplateBinding Padding}"
                          Background="{TemplateBinding Background}">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <ToggleButton
                                  x:Name="Expander"
                                  ClickMode="Press"
                                  IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                                  Style="{StaticResource ExpandCollapseToggleStyle}"
                             Visibility="{TemplateBinding HasItems,
                                                                 Converter={StaticResource BooleanToVisibilityConverter}}" />
                                    <ContentPresenter
                                  x:Name="PART_Header"
                                  Grid.Column="1"
                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                  ContentSource="Header" />
                                </Grid>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" Margin="30,0,0,0" />
                        </StackPanel>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="False">
                                <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
                            </Trigger>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="Bd" Property="Background" Value="Transparent" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Window.Resources>
    <Grid>
        <TreeView ItemsSource="{Binding TreeDataCollection}">
            <TreeView.ItemContainerStyle>
                <Style BasedOn="{StaticResource Style_Default_TreeViewItem}" TargetType="TreeViewItem" />
            </TreeView.ItemContainerStyle>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
                        <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsChecked}" />
                        <TextBlock  VerticalAlignment="Center" Text="{Binding Name}" PreviewMouseDown="TextBlock_PreviewMouseDown" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>

</Window>

 

using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ViewTreeDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void TextBlock_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (sender is System.Windows.Controls.TextBlock textBlock && e.ClickCount == 1)
            {
                var parent = textBlock.Parent as StackPanel;
                if (parent != null)
                {
                    var checkBox = parent.Children[0] as System.Windows.Controls.CheckBox;
                    if (checkBox != null)
                    {
                        checkBox.IsChecked = !checkBox.IsChecked;
                    }
                }
            }
        }
    }
}

 

posted @ 2025-06-27 16:05  昂昂呀  阅读(12)  评论(0)    收藏  举报