这里只对最近使用到的分层树做一些记录,有复选框示例,支持父级选中状态改变子集同步变化
废话不多说,直接上源码
View布局
<TreeDataGrid Height="710" BorderBrush="Gray" CanUserResizeColumns="False" FontSize="16" Source="{Binding TreeDataGridSource}"> <TreeDataGrid.Resources> <DataTemplate x:Key="CheckBoxCellTemplate" x:DataType="vm:TreeDataModel"> <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" /> </DataTemplate> </TreeDataGrid.Resources> </TreeDataGrid>
model对象
public class TreeDataModel : INotifyPropertyChanged { private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged(nameof(Name)); } } private int? _age; public int? Age { get { return _age; } set { _age = value; OnPropertyChanged(nameof(Age)); } } public bool _isSelected; public bool IsSelected { get { return _isSelected; } set { _isSelected = value; OnPropertyChanged(nameof(IsSelected)); // 如果是分类节点,更新所有子项的状态 if (IsCategory) { foreach (var child in Children) { child.IsSelected = value; } } } } private string _department; public string Department { get => _department; set { if (_department != value) { _department = value; OnPropertyChanged(nameof(Department)); } } } private string _category; public bool IsCategory => !string.IsNullOrEmpty(Category); public bool IsExpanded { get; set; } = true; public string Category { get { return _category; } set { _category = value; OnPropertyChanged(nameof(Category)); } } private ObservableCollection<TreeDataModel> _children = new ObservableCollection<TreeDataModel>(); public ObservableCollection<TreeDataModel> Children { get { return _children; } set { _children = value; OnPropertyChanged(nameof(Children)); } } public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } }
vm 数据刷新逻辑
private HierarchicalTreeDataGridSource<TreeDataModel> _treeDataGridSource = new HierarchicalTreeDataGridSource<TreeDataModel>(new List<TreeDataModel>()); public HierarchicalTreeDataGridSource<TreeDataModel> TreeDataGridSource { get { return _treeDataGridSource; } set { _treeDataGridSource = value; OnPropertyChanged(nameof(TreeDataGridSource)); } } private TreeDataModel selectedItem; public TreeDataModel SelectedItem { get => selectedItem; set { selectedItem = value; OnPropertyChanged(nameof(SelectedItem)); } } public async void InitTreeData() { List<TreeDataModel> data = new List<TreeDataModel>(); for (int i = 0; i < 10; i++) { int value = 10 + i; data.Add(new TreeDataModel() { Age = value, Name = $"示例{value}", Department = "测试部", }); } for (int i = 0; i < 7; i++) { int value = 10 + i; data.Add(new TreeDataModel() { Age = value, Name = $"张三{value}", Department = "研发", }); } for (int i = 23; i < 50; i++) { int value = 10 + i; data.Add(new TreeDataModel() { Age = value, Name = $"酒香也怕巷子深{value}", Department = "心如止水", }); } //聚合形成树形数据集 var categoryGroups = data .GroupBy(i => { // 按照"-"前面的部分分类 var dashIndex = i.Department.IndexOf('-'); return dashIndex > 0 ? i.Department.Substring(0, dashIndex) : i.Department; }); List<TreeDataModel> listData = new List<TreeDataModel>(); foreach (var group in categoryGroups) { var category = new TreeDataModel { Category = group.Key }; foreach (var item in group) { category.Children.Add(item); } listData.Add(category); } TreeDataGridSource = new HierarchicalTreeDataGridSource<TreeDataModel>(listData) { Columns = { new HierarchicalExpanderColumn<TreeDataModel>( new TextColumn<TreeDataModel, string>( "人员", x => x.IsCategory ? $"{x.Category}({x.Children.Count}成员)" : x.Name, GridLength.Auto) , x => x.Children, isExpandedSelector: x => x.IsExpanded), new TextColumn<TreeDataModel, int?>( "年龄", x => x.Age, GridLength.Auto), new TemplateColumn<TreeDataModel>( "", "CheckBoxCellTemplate", // 使用自定义模板 width: GridLength.Auto), }, }; TreeDataGridSource.RowSelection.SelectionChanged += (s, e) => { if (s is TreeDataGridRowSelectionModel<TreeDataModel> selec) { if (selec.SelectedItem is TreeDataModel selectModel) { SelectedItem = selectModel; } } }; }
关键点,
1.数据格式绑定基本都在vm完成treedatagrid数据源集合需要绑定HierarchicalTreeDataGridSource,而不是传统的ObservableCollection
2.行选中时间SelectionChanged,控件不支持直接使用SelectionChanged,需要vm业务层订阅RowSelection来引出SelectionChanged,数据集每次变化都需要重新订阅
3.复选框:
数据集Columns绑定时其实有CheckBoxColumn列,不过这里演示通过绑定资源键的形式 x:Key=”CheckBoxCellTemplate”实现复选框
父级状态改变影响子集,Avalonia原本的TreeDataGrid并没有对这个实现,但是自己可以通过通过对IsSelected属性变化时,验证IsCategory是否是分类节点 从而更改子集选中状态