• WPF:带复选框CheckBox的树TreeView


    最近要用WPF写一个树,同事给了我一个Demo(不知道是从哪里找来的),我基本上就是参照了这个Demo。

    先放一下效果图(3棵树):

     

    这个树索要满足的条件是:

    1. 父节点.Checked=true时,子节点全部选中(反之成立);
    2. 父节点.Checked=false时,子节点全部不选中(反之成立);
    3. 子节点存在部分节点选中,部分节点未选中时,父节点为非全选状态(null)(反之成立);

    那么这个树究竟要怎么造出来呢?

    由于用WPF,且用MVVM模式,故TreeView的ItemSource及复选框的选中状态IsChecked需要从ViewModel层进行绑定。先看一下树的xaml:

    <Window x:Class="MyWpfCheckTreeDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:VM="clr-namespace:MyWpfCheckTreeDemo.AppViewModel"
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            Title="MainWindow" Height="350" Width="525" Loaded="LoadedEvent">
        <Window.Resources>
            <HierarchicalDataTemplate x:Key="MyTreeItemTemplate" 
                                      DataType="{x:Type VM:CommonTreeView}" 
                                      ItemsSource="{Binding Path=Children,Mode=OneWay}">
                <StackPanel x:Name="My_SP"  Orientation="Horizontal" Margin="2">
                    <CheckBox  IsChecked="{Binding Path=IsChecked}" >
                    </CheckBox>
                    <ContentPresenter  Content="{Binding Path=NodeName,Mode=OneTime}" Margin="2,0"/>
                </StackPanel>
            </HierarchicalDataTemplate>
            <Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="True" />
            </Style>
        </Window.Resources>
        <Grid>
           <TreeView Grid.Row="1"  x:Name="tv" ItemsSource="{Binding MyTrees}" 
                          ItemContainerStyle="{StaticResource TreeViewItemStyle}"
                          ItemTemplate="{StaticResource MyTreeItemTemplate}">
                    </TreeView>
                </Grid>
           </Window>
    TreeView.xaml

    在xaml中的数据绑定好之后,就是在后台如何实现数据的传递了。

    先来看一下每个节点所需要包含的数据:

    • 节点名称:NodeName,
    • 父节点:Parent ,
    • 该父节点的所有孩子:Children,为每一个节点增加创建孩子的方法
    • 每个节点的选中状态:IsChecked,每次子节点的IsChecked变化时,需要去更新Parent节点的IsChecked.

    现在将上述数据封装成一个树节点类:

        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;
            } 
        }
    CommonTreeView.cs

    节点值变化时对UI进行通知的方法PropertyNotify:

        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;
            }
        }
    INotifyPropertyChanged

    到这里基本上这个树就可以投入使用了,现在在ViewModel中给树赋值,并取出选中状态的叶子的节点名称:
    这个里面的方法只给出重点部分:

      /// <summary>
            /// 创建树
            /// </summary>
            /// <returns></returns>
            public List<CommonTreeView> MyCreateTree()
            {
                List<CommonTreeView> views = new List<CommonTreeView>();
                //CommonTreeView _myT = new CommonTreeView("合约");
                CommonTreeView _myy = new CommonTreeView("CCP");
                views.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);
           }
    
            /// <summary>
            /// 选中的节点名称保存在_names中
            /// </summary>
            public void SaveC()
            {
                _names = new List<string>();
                //SelectedTree=MyTrees.FindAll(r => r.IsChecked == true); 
                foreach (CommonTreeView tree in MyTrees)
                {
                    GetCheckedItems(tree);
                }
             }
    
            /// <summary>
            /// 获取选中项
            /// </summary>
            /// <param name="tree"></param>
            private void GetCheckedItems(CommonTreeView tree)
            {
                if (tree.Parent != null && (tree.Children == null || tree.Children.Count == 0))
                {
                    if (tree.IsChecked.HasValue && tree.IsChecked == true)
                        _names.Add(tree.NodeName);
                }
                else if (tree.Children != null && tree.Children.Count > 0)
                {
                    foreach (CommonTreeView tv in tree.Children)
                        GetCheckedItems(tv);
                }
              }
    TreeViewModel.cs

    到这里就结束啦。。。

  • 相关阅读:
    php 发送超大数据处理
    Linux 忘记了mysql 密码
    利用mysqldump 实现每天备份方案
    window 下忘记了mysql 密码的解决方法
    MySQL添加字段和修改字段的方法
    php 即使客户端或者服务器断开(如关掉浏览器)脚本也可以继续执行
    PHP面向对象05_接口与多态
    PHP面向对象06_异常处理
    PHP面向对象07_PDO
    PHP面向对象之魔术方法复习
  • 原文地址:https://www.cnblogs.com/YunGy/p/4794124.html
Copyright © 2020-2023  润新知