Loading

WPF TreeView级联复选

blog-hbh-hc-header

目标效果

初始加载

选中叶子节点

操作中间节点

实现思路

TreeView数据源集合的子项ViewModel部分

BindableBasePrism中对应的Vm基类,读者也可以按自身需求实现属性通知接口(INotifyPropertyChanged)的基类。

    public class ModuleNode:BindableBase
    {

        //public bool IsChecked { get; set; } //属性需要设置为可空类型
		//复选框状态
        private bool? isChecked;
        public bool? IsChecked
        {
            get { return isChecked; }
            set { SetProperty(ref isChecked, value); }
        }
        
        // 子节点集合
        public List<ModuleNode> Children { get; set; }
		// 父节点对象
        public ModuleNode Parent { get; set; }
    }

主页面ViewModel

public class MainViewModel:BindableBase
{
	public DelegateCommand<ModuleNode> ModuleCkCommand { get; set; }
	public MainViewModel()
	{
		ModuleCkCommand = new DelegateCommand<ModuleNode>(ModuleCkMethod);
	}
	
	/// <summary>
    /// 模块选中函数
    /// </summary>
    /// <param name="node">操作选中对象</param>
    private void ModuleCkMethod(ModuleNode node)
    {
        // 子节点level-1总数
        int childCount = node.Children.Count;
        // 选中节点状态
        Debug.WriteLine($"更新选中项:{node.Name} {node.IsChecked}");
        // 选中子节点更新
        if (childCount > 0)
        {
            // 节点状态
            bool? ischeck = node.IsChecked;
            foreach (var child in node.Children)
            {
                child.IsChecked = ischeck;
                Debug.WriteLine($"更新子项:{child.Name} {child.IsChecked}");
                // 递归操作子节点下层
                ModuleCkMethod(child);
            }
        }
        // 父级节点更新
        ModuleParentCkMethod(node.Parent);
    }
    
   /// <summary>
   /// 更新父级节点状态
   /// </summary>
   /// <param name="parent">父级对象</param>
   private void ModuleParentCkMethod(ModuleNode parent)
   {
       if (parent == null)
       {
           return;
       }

       // 子集深度level-1 选中项 非选中项 null状态
       int childCount = parent.Children.Count;
       int ckCount = parent.Children.Count(child => child.IsChecked == true);
       int uckCount = parent.Children.Count(child => child.IsChecked == false);
       int nullCount = parent.Children.Count(child => child.IsChecked == null);
       // 子节点是否存在null,若存在则父级节点直接设置为null
       if (nullCount != 0)
       {
           parent.IsChecked = null;
       }
       else // 判定选中和非选中是否与下级子节点总数一致,一致时,则更新父节点状态true/false
       {
           if (ckCount == childCount)
           {
               parent.IsChecked = true;
           }
           else if (uckCount == childCount)
           {
               parent.IsChecked = false;
           }
           // 判定选中和非选中是否与下级子节点总数一致,不一致时,则更新父节点状态为null
           else
           {
               parent.IsChecked = null;
           }
       }
       Debug.WriteLine($"更新父级:{parent.Name} {parent.IsChecked}");
       // 递归更新父级状态
       ModuleParentCkMethod(parent.Parent);
   }
}

UI部分

其中需要注意的是CheckBox对应的启用三状态(True、False、null)属性IsThreeState,默认为False,不启用。

        <TreeView x:Name="TreeData" ItemsSource="{Binding TreeItemSources}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="{Binding Path=IsChecked}" Command="{Binding DataContext.ModuleCkCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeView}}}" 
      CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=DataContext}">
                        </CheckBox>
                        <ContentPresenter VerticalAlignment="Center" Content="{Binding Path=Name}" Margin="5,0"/>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
posted @ 2024-03-10 23:18  关关长语  阅读(131)  评论(0编辑  收藏  举报