Files
ShrlAlgoToolkit/NeoUI/Melskin/Controls/TreeGrid.xaml.cs

162 lines
7.5 KiB
C#
Raw Normal View History

2025-08-24 13:49:55 +08:00
using System.Collections;
2026-01-02 17:30:30 +08:00
namespace VariaStudio.Controls
2025-08-24 13:49:55 +08:00
{
// 定义模板中必须存在的部件,这是良好的自定义控件开发实践
/// <summary>
/// TreeGrid 控件是一个自定义控件,它结合了树形结构和表格布局的功能。通过此控件,用户可以展示具有层次结构的数据,并且能够以类似于表格的方式组织列。
/// </summary>
/// <remarks>
/// 该控件支持数据绑定、列定义以及行模板的自定义允许灵活地展示复杂的数据结构。TreeGrid 内部使用了 ScrollViewer 和 TreeView 作为其主要组成部分来实现滚动和树形视图功能。
/// </remarks>
[TemplatePart(Name = "PART_HeaderScrollViewer", Type = typeof(ScrollViewer))]
[TemplatePart(Name = "PART_ContentTreeView", Type = typeof(TreeView))]
public class TreeGrid : Control
{
private ScrollViewer? headerScrollViewer;
private ScrollViewer? contentScrollViewer;
private TreeView? contentTreeView;
#region Dependency Properties
// 1. ItemsSource (IEnumerable) - 用于绑定数据源
/// <summary>
/// 表示 TreeGrid 控件的数据源的依赖属性,用于绑定到一个实现了 IEnumerable 接口的数据集合。
/// 通过设置此属性,可以将具有层次结构或简单列表形式的数据绑定到控件上,从而在 TreeGrid 中展示数据。支持任何类型的数据源,只要它能够被枚举。
/// </summary>
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(TreeGrid), new PropertyMetadata(null));
/// <summary>
///
/// </summary>
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
// 2. Columns (GridViewColumnCollection) - 直接使用WPF内置的列集合
/// <summary>
/// 表示 TreeGrid 控件中定义的列集合的依赖属性,用于指定显示在控件中的各个列及其属性。
/// 通过此属性可以添加、删除或修改列,例如设置列标题、绑定数据成员等。每个列都由一个 GridViewColumn 对象表示。
/// </summary>
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(nameof(Columns), typeof(GridViewColumnCollection), typeof(TreeGrid), new PropertyMetadata(new GridViewColumnCollection()));
/// <summary>
/// 表示 TreeGrid 控件中定义的列集合,用于指定显示在控件中的各个列及其属性。
/// 通过此属性可以添加、删除或修改列,例如设置列标题、绑定数据成员等。每个列都由一个 GridViewColumn 对象表示。
/// </summary>
public GridViewColumnCollection Columns
{
get => (GridViewColumnCollection)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
// 3. ItemTemplate (HierarchicalDataTemplate) - 用户自定义行和子项的模板
/// <summary>
/// 表示用于设置或获取 TreeGrid 控件中每个项的数据模板的依赖属性。
/// 通过此属性,用户可以自定义 TreeViewItem 的数据展示方式,例如通过 HierarchicalDataTemplate 定义复杂的数据结构展示。
/// </summary>
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register(nameof(ItemTemplate), typeof(HierarchicalDataTemplate), typeof(TreeGrid), new PropertyMetadata(null));
/// <summary>
///
/// </summary>
public HierarchicalDataTemplate ItemTemplate
{
get => (HierarchicalDataTemplate)GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
// 4. ItemContainerStyle (Style) - 允许用户自定义 TreeViewItem 的样式
/// <summary>
/// 表示用于设置或获取 TreeGrid 控件中每个项的容器样式(即 TreeViewItem 的样式)的依赖属性。
/// 通过此属性,用户可以自定义 TreeViewItem 的外观,例如修改其背景色、边框样式等视觉效果。
/// </summary>
public static readonly DependencyProperty ItemContainerStyleProperty =
DependencyProperty.Register(nameof(ItemContainerStyle), typeof(Style), typeof(TreeGrid), new PropertyMetadata(null));
/// <summary>
/// 获取或设置应用于 TreeGrid 控件中每个项容器(即 TreeViewItem的样式。
/// 通过此属性,可以自定义 TreeViewItem 的外观,例如背景色、边框样式等视觉效果。
/// </summary>
public Style ItemContainerStyle
{
get => (Style)GetValue(ItemContainerStyleProperty);
set => SetValue(ItemContainerStyleProperty, value);
}
// 也可以添加 SelectedItem, CornerRadius 等其他 BUI 库中有的属性...
#endregion
static TreeGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeGrid), new FrameworkPropertyMetadata(typeof(TreeGrid)));
}
// 当控件的模板应用时,此方法被调用
/// <inheritdoc />
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// 解除旧的事件处理器,防止内存泄漏
if (contentScrollViewer != null)
contentScrollViewer.ScrollChanged -= OnContentScrollChanged;
// 从模板中获取关键部件
headerScrollViewer = GetTemplateChild("PART_HeaderScrollViewer") as ScrollViewer;
contentTreeView = GetTemplateChild("PART_ContentTreeView") as TreeView;
if (contentTreeView != null)
{
// TreeView 加载完成后,查找其内部的 ScrollViewer
contentTreeView.Loaded += (_, _) =>
{
contentScrollViewer = FindVisualChild<ScrollViewer>(contentTreeView);
if (contentScrollViewer != null)
{
// 【核心】挂载滚动同步事件
contentScrollViewer.ScrollChanged += OnContentScrollChanged;
}
};
}
}
// 当内容滚动时,同步表头的水平滚动位置
private void OnContentScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (headerScrollViewer != null && e.HorizontalChange != 0)
{
headerScrollViewer.ScrollToHorizontalOffset(e.HorizontalOffset);
}
}
// 辅助方法:在可视化树中查找指定类型的子元素
private static T? FindVisualChild<T>(DependencyObject? obj) where T : DependencyObject
{
if (obj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T typedChild)
{
return typedChild;
}
T? childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
return null;
}
}
}