(关注博主后,在“粉丝专栏”,可免费阅读此文)
wpf的功能非常强大,很多控件都是原生的,但是要使用TreeView+DataGrid的组合,就需要我们自己去封装实现。
我们需要的效果如图所示:
这2个图都是第三方控件自带的,并且都是收费使用。
现在我们就用原生的控件进行封装一个。
本文源码效果如下,(搞了好几天,的确有难度,所以源码也收费,便宜,赚点辛苦费)
功能如图所示, 目前已经实现了一部分。
首先说明一下,实现上面的效果,有3种方法
第一种:技术的选择是TreeView(也就是本文的演示)。
第二种:技术的选择是DataGrid。
第三种:技术的选择是ListView。
本文演示的是使用TreeView的实现。
1.首先建立一个wpf程序
2. 封装TreeGrid
namespace TreeView.TreeDataGrid.Controls
{//这里有一个骚操作,就是把引用放在里面using System;using System.Collections.Specialized;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Media;public class TreeGrid : TreeView{static TreeGrid(){DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeGrid), new FrameworkPropertyMetadata(typeof(TreeGrid)));}#region ColumnMappings DependencyPropertypublic string ColumnMappings{get { return (string)GetValue(ColumnMappingsProperty); }set { SetValue(ColumnMappingsProperty, value); }}public static readonly DependencyProperty ColumnMappingsProperty =DependencyProperty.Register("ColumnMappings", typeof(string), typeof(TreeGrid),new PropertyMetadata("", new PropertyChangedCallback(TreeGrid.OnColumnMappingsPropertyChanged)));private static void OnColumnMappingsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnColumnMappingsValueChanged();}}protected void OnColumnMappingsValueChanged(){if (!string.IsNullOrEmpty(ColumnMappings)){ResetMappingColumns(ColumnMappings);}}private void ResetMappingColumns(string mapping){GridViewColumnCollection items = new GridViewColumnCollection();var columns = mapping.Split(new char[] { ';', '|' }, StringSplitOptions.RemoveEmptyEntries);foreach (var c in columns){var index = c.IndexOf(':');var title = "";var name = "";if (index > 0){title = c.Substring(0, index);name = c.Substring(index + 1);}else{title = c;name = c;}DataTemplate temp = null;var res = this.FindTreeResource<DataTemplate>(name);if (res != null && res is DataTemplate template){temp = template;}else{temp = new DataTemplate();FrameworkElementFactory element = null;if (items.Count == 0){element = new FrameworkElementFactory(typeof(TreeItemContentControl));element.SetValue(ContentControl.ContentProperty, new Binding(name));}else{element = new FrameworkElementFactory(typeof(TreeGridCell));element.SetValue(ContentControl.ContentProperty, new Binding(name));}temp.VisualTree = element;}var col = new GridViewColumn{Width = 200,Header = title,CellTemplate = temp,};items.Add(col);}Columns = items;}#endregion#region Columns DependencyPropertypublic GridViewColumnCollection Columns{get { return (GridViewColumnCollection)GetValue(ColumnsProperty); }set { SetValue(ColumnsProperty, value); }}public static readonly DependencyProperty ColumnsProperty =DependencyProperty.Register("Columns", typeof(GridViewColumnCollection), typeof(TreeGrid),new PropertyMetadata(null, new PropertyChangedCallback(TreeGrid.OnColumnsPropertyChanged)));private static void OnColumnsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnColumnsValueChanged();}}protected void OnColumnsValueChanged(){}#endregion#region RowHeight DependencyPropertypublic double RowHeight{get { return (double)GetValue(RowHeightProperty); }set { SetValue(RowHeightProperty, value); }}public static readonly DependencyProperty RowHeightProperty =DependencyProperty.Register("RowHeight", typeof(double), typeof(TreeGrid),new PropertyMetadata(30.0, new PropertyChangedCallback(TreeGrid.OnRowHeightPropertyChanged)));private static void OnRowHeightPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnRowHeightValueChanged();}}protected void OnRowHeightValueChanged(){}#endregion#region ShowCellBorder DependencyPropertypublic bool ShowCellBorder{get { return (bool)GetValue(ShowCellBorderProperty); }set { SetValue(ShowCellBorderProperty, value); }}public static readonly DependencyProperty ShowCellBorderProperty =DependencyProperty.Register("ShowCellBorder", typeof(bool), typeof(TreeGrid),new PropertyMetadata(false, new PropertyChangedCallback(TreeGrid.OnShowCellBorderPropertyChanged)));private static void OnShowCellBorderPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnShowCellBorderValueChanged();}}protected void OnShowCellBorderValueChanged(){}#endregion#region IconStroke DependencyPropertypublic Brush IconStroke{get { return (Brush)GetValue(IconStrokeProperty); }set { SetValue(IconStrokeProperty, value); }}public static readonly DependencyProperty IconStrokeProperty =DependencyProperty.Register("IconStroke", typeof(Brush), typeof(TreeGrid),new PropertyMetadata(new SolidColorBrush(Colors.LightGray), new PropertyChangedCallback(TreeGrid.OnIconStrokePropertyChanged)));private static void OnIconStrokePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnIconStrokeValueChanged();}}protected void OnIconStrokeValueChanged(){}#endregion#region CellBorderBrush DependencyPropertypublic Brush CellBorderBrush{get { return (Brush)GetValue(CellBorderBrushProperty); }set { SetValue(CellBorderBrushProperty, value); }}public static readonly DependencyProperty CellBorderBrushProperty =DependencyProperty.Register("CellBorderBrush", typeof(Brush), typeof(TreeGrid),new PropertyMetadata(new SolidColorBrush(Colors.LightGray), new PropertyChangedCallback(TreeGrid.OnCellBorderBrushPropertyChanged)));private static void OnCellBorderBrushPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnCellBorderBrushValueChanged();}}protected void OnCellBorderBrushValueChanged(){}#endregionprotected override DependencyObject GetContainerForItemOverride(){return new TreeGridItem();}protected override bool IsItemItsOwnContainerOverride(object item){return item is TreeGridItem;}protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e){base.OnItemsChanged(e);}}public class TreeGridItem : TreeViewItem{public event EventHandler IconStateChanged;static TreeGridItem(){DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeGridItem), new FrameworkPropertyMetadata(typeof(TreeGridItem)));}public TreeGridItem(){this.DataContextChanged += TreeGridItem_DataContextChanged;}private void TreeGridItem_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e){if (DataContext != null && DataContext is TreeItemData treeData){this.SetBinding(IsExpandedProperty, new Binding("IsExpanded") { Source = treeData, Mode = BindingMode.TwoWay });}}protected override void OnVisualParentChanged(DependencyObject oldParent){base.OnVisualParentChanged(oldParent);}protected override DependencyObject GetContainerForItemOverride(){return new TreeGridItem();}protected override bool IsItemItsOwnContainerOverride(object item){return item is TreeGridItem;}}/** https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/GridViewRowPresenter.cs,ace7d38fc902993d* GridViewRow里的每个元素,增加了一个默认的Margin,这样在设置边框的时候会比较麻烦,在运行时去掉*/public class TreeGridCell : ContentControl{static TreeGridCell(){DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeGridCell), new FrameworkPropertyMetadata(typeof(TreeGridCell)));}public TreeGridCell(){Loaded += TreeGridCell_Loaded;}private void TreeGridCell_Loaded(object sender, RoutedEventArgs e){Loaded -= TreeGridCell_Loaded;var p = VisualTreeHelper.GetParent(this);if (p != null && p is FrameworkElement f && f.Margin.Left > 0){f.Margin = new Thickness(0);}}}public static class TreeHelper{public static T FindParent<T>(this DependencyObject obj){var p = VisualTreeHelper.GetParent(obj);if (p == null){return default(T);}if (p is T tt){return tt;}return FindParent<T>(p);}public static T FindTreeResource<T>(this FrameworkElement obj, string key){if (obj == null){return default(T);}var r = obj.TryFindResource(key);if (r == null){r = Application.Current.TryFindResource(key);}if (r != null && r is T t){return t;}var p = FindParent<FrameworkElement>(obj);if (p != null){return FindTreeResource<T>(p, key);}return default(T);}}
}
3.TreeGrid.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:TreeView.TreeDataGrid.Controls"><SolidColorBrush x:Key="TreeIconStroke" Color="GreenYellow" /><Style x:Key="TreeGridItemStyle" TargetType="{x:Type local:TreeGridItem}"><Setter Property="Foreground" Value="Black"/><Setter Property="Background" Value="Transparent"/><Setter Property="IsExpanded" Value="True"/><Setter Property="BorderBrush" Value="Wheat"/><Setter Property="BorderThickness" Value="0,0,0,1"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeGridItem}"><StackPanel><Border Name="Bd"Background="Transparent"BorderBrush="{TemplateBinding BorderBrush}"Padding="{TemplateBinding Padding}"><GridViewRowPresenter x:Name="PART_Header" Content="{TemplateBinding Header}" Columns="{Binding Path=Columns,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeGrid}}" /></Border><ItemsPresenter x:Name="ItemsHost" /></StackPanel><ControlTemplate.Triggers><Trigger Property="IsExpanded" Value="false"><Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/></Trigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="HasHeader" Value="false"/><Condition Property="Width" Value="Auto"/></MultiTrigger.Conditions><Setter TargetName="PART_Header" Property="MinWidth" Value="75"/></MultiTrigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="HasHeader" Value="false"/><Condition Property="Height" Value="Auto"/></MultiTrigger.Conditions><Setter TargetName="PART_Header" Property="MinHeight" Value="19"/></MultiTrigger><MultiTrigger><!--移动变色--><MultiTrigger.Conditions><Condition Property="IsFocused" Value="False"/><Condition SourceName="Bd" Property="IsMouseOver" Value="true"/></MultiTrigger.Conditions><Setter Property="Background" Value=" red" TargetName="Bd"/></MultiTrigger><Trigger Property="IsSelected" Value="true"><!--选中的背景颜色--><Setter TargetName="Bd" Property="Background" Value="YellowGreen"/><!--<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>--><Setter Property="Foreground" Value="Red"/></Trigger><!--隔行换色--><!--<Trigger Property="AlternationIndex" Value="0" ><Setter TargetName="Bd" Property="Background" Value="blue" /></Trigger><Trigger Property="AlternationIndex" Value="2" ><Setter TargetName="Bd" Property="Background" Value="black" /></Trigger>--><!--<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:TreeGrid}, Path=Columns.Count }" Value="0"><Setter TargetName="Bd" Property="Background" Value="#FFD3D3D3"/></DataTrigger><DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:TreeGrid}, Path=Columns.Count}" Value="2"><Setter TargetName="Bd" Property="Background" Value="#FFE6E6E6"/></DataTrigger>--><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsSelected" Value="true"/><Condition Property="IsSelectionActive" Value="false"/></MultiTrigger.Conditions><Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/></MultiTrigger><Trigger Property="IsEnabled" Value="false"><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter><Style.Triggers><!--隔行换色--><Trigger Property="AlternationIndex" Value="0" ><Setter Property="Background" Value="#e7e7e7" /></Trigger><Trigger Property="AlternationIndex" Value="1" ><Setter Property="Background" Value="#f2f2f2" /></Trigger></Style.Triggers></Style><Style TargetType="{x:Type local:TreeGridItem}" BasedOn="{StaticResource TreeGridItemStyle}"/><Style TargetType="{x:Type local:TreeGridCell}"><Setter Property="HorizontalContentAlignment" Value="Center"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="BorderBrush" Value="Red"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeGridCell}"><Border x:Name="CellBorder" Margin="0,0,-0.5,0"Background="{TemplateBinding Background}"BorderBrush="Red"BorderThickness="0,0,0,1"><ContentControl Content="{TemplateBinding Content}"ContentTemplate="{TemplateBinding ContentTemplate}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"VerticalAlignment="{TemplateBinding VerticalContentAlignment}"SnapsToDevicePixels="True"/></Border><!--BorderBrush="Red"下划线颜色--><!--<ControlTemplate.Triggers><DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeGrid},Path=ShowCellBorder}" Value="true"><Setter TargetName="CellBorder" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeGrid},Path=CellBorderBrush}" /></DataTrigger></ControlTemplate.Triggers>--></ControlTemplate></Setter.Value></Setter></Style><Style TargetType="{x:Type local:TreeGrid}"><Setter Property="IconStroke" Value="{StaticResource TreeIconStroke}"/><Setter Property="ItemContainerStyle" Value="{StaticResource {x:Type local:TreeGridItem}}"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeGrid}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="0"><!--最大边框--><DockPanel><!--标题栏--><GridViewHeaderRowPresenter IsHitTestVisible="False" Columns="{TemplateBinding Columns}" Height="{TemplateBinding RowHeight}" DockPanel.Dock="Top" ></GridViewHeaderRowPresenter><ItemsPresenter /></DockPanel></Border></ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>
4.代码很多,最终的源码格式
需要源码请联系我。
本文来源:
WPF组合控件TreeView+DataGrid之TreeView封装-CSDN博客