WPF 原生控件封装:TreeView 与 DataGrid 组合实现
WPF 的功能非常强大,许多控件都是原生的。但在实际开发中,有时候需要同时使用 TreeView 和 DataGrid 的组合效果(例如树形表格),这就需要我们去封装实现。
第三方控件虽然方便,但往往收费且功能受限。利用原生控件进行封装,既能满足需求又能保持轻量。本文演示如何使用 TreeView 来实现这一组合效果。
实现思路
实现上述效果主要有三种技术路径:
- TreeView:本文重点演示的方案,适合层级数据展示。
- DataGrid:适合纯表格数据。
- ListView:灵活性高但配置较繁琐。
我们选择第一种方案,通过继承 TreeView 并重写相关属性,模拟出类似 DataGrid 的列显示效果。
1. 创建 WPF 项目
首先建立一个标准的 WPF 程序项目,确保引用了必要的命名空间。
2. 封装 TreeGrid 核心类
我们需要创建一个自定义控件 TreeGrid,它继承自 TreeView。核心在于定义列映射、行高以及单元格样式。
using System;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace TreeView.TreeDataGrid.Controls
{
public class TreeGrid : TreeView
{
static TreeGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(TreeGrid),
new FrameworkPropertyMetadata(typeof(TreeGrid)));
}
#region ColumnMappings DependencyProperty
/// <summary>
/// 列映射字符串,格式如 "标题:字段名;标题:字段名"
///
ColumnMappings
{
{ ()GetValue(ColumnMappingsProperty); }
{ SetValue(ColumnMappingsProperty, ); }
}
DependencyProperty ColumnMappingsProperty =
DependencyProperty.Register(, (), (TreeGrid),
PropertyMetadata(, PropertyChangedCallback(OnColumnMappingsPropertyChanged)));
{
(obj TreeGrid grid)
{
grid.OnColumnMappingsValueChanged();
}
}
{
(!.IsNullOrEmpty(ColumnMappings))
{
ResetMappingColumns(ColumnMappings);
}
}
{
items = GridViewColumnCollection();
columns = mapping.Split([] { , }, StringSplitOptions.RemoveEmptyEntries);
( c columns)
{
index = c.IndexOf();
title = ;
name = ;
(index > )
{
title = c.Substring(, index);
name = c.Substring(index + );
}
{
title = c;
name = c;
}
DataTemplate temp = ;
res = .FindTreeResource<DataTemplate>(name);
(res != && res DataTemplate template)
{
temp = template;
}
{
temp = DataTemplate();
FrameworkElementFactory element = ;
(items.Count == )
{
element = FrameworkElementFactory((TreeItemContentControl));
element.SetValue(ContentControl.ContentProperty, Binding(name));
}
{
element = FrameworkElementFactory((TreeGridCell));
element.SetValue(ContentControl.ContentProperty, Binding(name));
}
temp.VisualTree = element;
}
col = GridViewColumn
{
Width = ,
Header = title,
CellTemplate = temp,
};
items.Add(col);
}
Columns = items;
}
GridViewColumnCollection Columns
{
{ (GridViewColumnCollection)GetValue(ColumnsProperty); }
{ SetValue(ColumnsProperty, ); }
}
DependencyProperty ColumnsProperty =
DependencyProperty.Register(, (GridViewColumnCollection), (TreeGrid),
PropertyMetadata(, PropertyChangedCallback(OnColumnsPropertyChanged)));
{
(obj TreeGrid grid)
{
grid.OnColumnsValueChanged();
}
}
{ }
RowHeight
{
{ ()GetValue(RowHeightProperty); }
{ SetValue(RowHeightProperty, ); }
}
DependencyProperty RowHeightProperty =
DependencyProperty.Register(, (), (TreeGrid),
PropertyMetadata(, PropertyChangedCallback(OnRowHeightPropertyChanged)));
{
(obj TreeGrid grid) grid.OnRowHeightValueChanged();
}
{ }
ShowCellBorder
{
{ ()GetValue(ShowCellBorderProperty); }
{ SetValue(ShowCellBorderProperty, ); }
}
DependencyProperty ShowCellBorderProperty =
DependencyProperty.Register(, (), (TreeGrid),
PropertyMetadata(, PropertyChangedCallback(OnShowCellBorderPropertyChanged)));
{
(obj TreeGrid grid) grid.OnShowCellBorderValueChanged();
}
{ }
Brush IconStroke
{
{ (Brush)GetValue(IconStrokeProperty); }
{ SetValue(IconStrokeProperty, ); }
}
DependencyProperty IconStrokeProperty =
DependencyProperty.Register(, (Brush), (TreeGrid),
PropertyMetadata( SolidColorBrush(Colors.LightGray), PropertyChangedCallback(OnIconStrokePropertyChanged)));
{
(obj TreeGrid grid) grid.OnIconStrokeValueChanged();
}
{ }
Brush CellBorderBrush
{
{ (Brush)GetValue(CellBorderBrushProperty); }
{ SetValue(CellBorderBrushProperty, ); }
}
DependencyProperty CellBorderBrushProperty =
DependencyProperty.Register(, (Brush), (TreeGrid),
PropertyMetadata( SolidColorBrush(Colors.LightGray), PropertyChangedCallback(OnCellBorderBrushPropertyChanged)));
{
(obj TreeGrid grid) grid.OnCellBorderBrushValueChanged();
}
{ }
{
TreeGridItem();
}
{
item TreeGridItem;
}
{
.OnItemsChanged(e);
}
}
:
{
EventHandler IconStateChanged;
{
DefaultStyleKeyProperty.OverrideMetadata(
(TreeGridItem),
FrameworkPropertyMetadata((TreeGridItem)));
}
{
.DataContextChanged += TreeGridItem_DataContextChanged;
}
{
(DataContext != && DataContext TreeItemData treeData)
{
.SetBinding(IsExpandedProperty, Binding() { Source = treeData, Mode = BindingMode.TwoWay });
}
}
{
.OnVisualParentChanged(oldParent);
}
{
TreeGridItem();
}
{
item TreeGridItem;
}
}
:
{
{
DefaultStyleKeyProperty.OverrideMetadata(
(TreeGridCell),
FrameworkPropertyMetadata((TreeGridCell)));
}
{
Loaded += TreeGridCell_Loaded;
}
{
Loaded -= TreeGridCell_Loaded;
p = VisualTreeHelper.GetParent();
(p != && p FrameworkElement f && f.Margin.Left > )
{
f.Margin = Thickness();
}
}
}
{
{
p = VisualTreeHelper.GetParent(obj);
(p == ) (T);
(p T tt) tt;
FindParent<T>(p);
}
{
(obj == ) (T);
r = obj.TryFindResource(key);
(r == ) r = Application.Current.TryFindResource(key);
(r != && r T t) t;
p = FindParent<FrameworkElement>(obj);
(p != ) FindTreeResource<T>(p, key);
(T);
}
}
}


