Files
ShrlAlgoToolkit/Melskin/Controls/TreeGrid.xaml.cs
2026-02-17 22:17:13 +08:00

162 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections;
namespace Melskin.Controls
{
// 定义模板中必须存在的部件,这是良好的自定义控件开发实践
/// <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;
}
}
}