WPF TreeView级联复选
目标效果
初始加载
选中叶子节点
操作中间节点
实现思路
TreeView
数据源集合的子项ViewModel
部分
BindableBase
为 Prism
中对应的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>