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