WPF 可多选的树TreeView

因工作需要,自定义一个树形控件,支持多选并且父节点选中子节点全部选中,反之全部取消

<Window x:Class="treeview.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:VM="clr-namespace:treeview"
        mc:Ignorable="d" 
    Title="MainWindow" Height="350" Width="525" >
    <Grid.Resources>
            <Style TargetType="StackPanel" x:Key="ToolTipStyle">
                <Style.Triggers>
                    <Trigger Property="ToolTip" Value="{x:Static sys:String.Empty}">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </Trigger>
                </Style.Triggers>
            </Style>
            <HierarchicalDataTemplate x:Key="MyTreeItemTemplate"
                                  DataType="{x:Type sctrls:CommonTreeView}" 
                                  ItemsSource="{Binding Path=Children,Mode=OneWay}">
                <StackPanel x:Name="My_SP" Style="{StaticResource ResourceKey=ToolTipStyle}"  Orientation="Horizontal" Margin="2" ToolTip="{Binding NodeTag}">
                    <CheckBox   IsChecked="{Binding Path=IsChecked}" >
                    </CheckBox>
                    <TextBlock>
                    <ContentPresenter  Content="{Binding Path=NodeName,Mode=OneTime}" Margin="2,0"/>
                    </TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
            <Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="False" />
            </Style>
        </Grid.Resources>
    <Grid >
        <TreeView Grid.Row="1"  x:Name="tv" ItemsSource="{Binding MyTrees}" 
                      ItemContainerStyle="{StaticResource TreeViewItemStyle}"
                      ItemTemplate="{StaticResource MyTreeItemTemplate}">
        </TreeView>
    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Text;

namespace treeview
{
    public class NotifyPropertyBase : INotifyPropertyChanged
    {
        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }

    /// <summary>
    /// 扩展方法
    /// 避免硬编码问题
    /// </summary>
    public static class NotifyPropertyBaseEx
    {
        public static void SetProperty<T, U>(this T tvm, Expression<Func<T, U>> expre) where T : NotifyPropertyBase, new()
        {
            string _pro = CommonFun.GetPropertyName(expre);
            tvm.OnPropertyChanged(_pro);
        }//为什么扩展方法必须是静态的
    }

    public class CommonFun
    {
        /// <summary>
        /// 返回属性名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static string GetPropertyName<T, U>(Expression<Func<T, U>> expr)
        {
            string _propertyName = "";
            if (expr.Body is MemberExpression)
            {
                _propertyName = (expr.Body as MemberExpression).Member.Name;
            }
            else if (expr.Body is UnaryExpression)
            {
                _propertyName = ((expr.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
            }
            return _propertyName;
        }
    } 
}
using System;
using System.Collections.Generic;
using System.Text;

namespace treeview
{
    public class CommonTreeView : NotifyPropertyBase
    {
        /// <summary>
        /// 父
        /// </summary>
        public CommonTreeView Parent
        {
            get;
            set;
        }

        /// <summary>
        /// 子
        /// </summary>
        public List<CommonTreeView> Children
        {
            get;
            set;
        }

        /// <summary>
        /// 节点的名字
        /// </summary>
        public string NodeName
        {
            get;
            set;
        }

        public bool? _isChecked;
        /// <summary>
        /// CheckBox是否选中
        /// </summary>
        public bool? IsChecked
        {
            get
            {
                return _isChecked;
            }
            set
            {
                SetIsChecked(value, true, true);
            }
        }

        public CommonTreeView(string name)
        {
            this.NodeName = name;
            this.Children = new List<CommonTreeView>();
        }
        public CommonTreeView() { }


        private void SetIsChecked(bool? value, bool checkedChildren, bool checkedParent)
        {
            if (_isChecked == value) return;
            _isChecked = value;
            //选中和取消子类
            if (checkedChildren && value.HasValue && Children != null)
                Children.ForEach(ch => ch.SetIsChecked(value, true, false));

            //选中和取消父类
            if (checkedParent && this.Parent != null)
                this.Parent.CheckParentCheckState();

            //通知更改
            this.SetProperty(x => x.IsChecked);
        }

        /// <summary>
        /// 检查父类是否选 中
        /// 如果父类的子类中有一个和第一个子类的状态不一样父类ischecked为null
        /// </summary>
        private void CheckParentCheckState()
        {
            List<CommonTreeView> checkedItems = new List<CommonTreeView>();
            string checkedNames = string.Empty;
            bool? _currentState = this.IsChecked;
            bool? _firstState = null;
            for (int i = 0; i < this.Children.Count; i++)
            {
                bool? childrenState = this.Children[i].IsChecked;
                if (i == 0)
                {
                    _firstState = childrenState;
                }
                else if (_firstState != childrenState)
                {
                    _firstState = null;
                }
            }
            if (_firstState != null) _currentState = _firstState;
            SetIsChecked(_firstState, false, true);
        }

        /// <summary>
        /// 创建树
        /// </summary>
        /// <param name="children"></param>
        /// <param name="isChecked"></param>

        public void CreateTreeWithChildre(CommonTreeView children, bool? isChecked)
        {
            this.Children.Add(children);
            //必须先把孩子加入再为Parent赋值,
            //否则当只有一个子节点时Parent的IsChecked状态会出错

            children.Parent = this;
            children.IsChecked = isChecked;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;

namespace treeview
{
    public class CommonTreeViewModel
    {
        public ObservableCollection<CommonTreeView> myTrees = new ObservableCollection<CommonTreeView>();

        public CommonTreeViewModel() {

            CommonTreeView _myy = new CommonTreeView("CCP");
            MyTrees.Add(_myy);
            CommonTreeView _myy1 = new CommonTreeView("CCP_1");
            _myy.CreateTreeWithChildre(_myy1, true);
            CommonTreeView _myy1_1 = new CommonTreeView("CCP_1.1");
            _myy1.CreateTreeWithChildre(_myy1_1, true);
            CommonTreeView _myy1_2 = new CommonTreeView("CCP_1.1");
            _myy1.CreateTreeWithChildre(_myy1_2, true);

            CommonTreeView _myy2 = new CommonTreeView("CCP_1");
            _myy.CreateTreeWithChildre(_myy2, true);

            _myy2.CreateTreeWithChildre(_myy1_1, true);
            _myy2.CreateTreeWithChildre(_myy1_2, true);
        }

        public ObservableCollection<CommonTreeView> MyTrees
        {
            get { return myTrees; }
            set { myTrees = value; }
        
        }
    }
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 treeview
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            CommonTreeViewModel model = new CommonTreeViewModel();
            this.tv.DataContext = model;

        }
    }
}

需要注意的是

1、 集合必须使用ObservableCollection,因为ObservableCollection继承了INotifyPropertyChanged,集合改变时会自动通知改变页面

2、集合MyTrees必须实现Get,Set方法 因为在C#中,写了{get;set;}才是属性,不写就是字段。而Binding的Source是对象,Path必须是属性。

WPF TreeView 可以通过设置 SelectionMode 属性来启用多选模式。默认情况下,TreeView 的 SelectionMode 属性是 Single,只能选择一个节点。如果要启用多选模式,可以将 SelectionMode 属性设置为 Extended。 以下是一个简单的示例,演示如何在 WPF TreeView 中启用多选模式: ```xml <TreeView ItemsSource="{Binding Items}" SelectionMode="Extended"> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Name}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> ``` 在上面的示例中,我们将 TreeView 的 SelectionMode 属性设置为 Extended,使其启用多选模式。然后,我们使用 TreeViewItem 的样式将其 IsSelected 属性绑定到 ViewModel 中的 IsSelected 属性。最后,我们使用 HierarchicalDataTemplate 定义 TreeView节点模板。 ViewModel 代码如下: ```csharp public class ItemViewModel : INotifyPropertyChanged { public string Name { get; set; } public ObservableCollection<ItemViewModel> Children { get; set; } private bool _isSelected; public bool IsSelected { get { return _isSelected; } set { if (_isSelected != value) { _isSelected = value; OnPropertyChanged(nameof(IsSelected)); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } ``` 在 ViewModel 中,我们定义了一个 IsSelected 属性,并在 TreeViewItem 的样式中将其绑定到 TreeView 中的 IsSelected 属性。当用户选择或取消选择节点时,ViewModel 中的 IsSelected 属性将更新。 希望这个示例可以帮助你实现 WPF TreeView多选模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这个月太忙没时间看C++

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值