这里只对最近使用到的分层树做一些记录,有复选框示例,支持父级选中状态改变子集同步变化
废话不多说,直接上源码
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是否是分类节点 从而更改子集选中状态