WPF组合控件TreeView+DataGrid之TreeView封装

发布时间:2023年12月22日

(关注博主后,在“粉丝专栏”,可免费阅读此文)? ? ? ??

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 DependencyProperty
        public 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 DependencyProperty
        public 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 DependencyProperty
        public 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 DependencyProperty
        public 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 DependencyProperty
        public 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 DependencyProperty
        public 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()
        {

        }
        #endregion

        protected 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博客

文章来源:https://blog.csdn.net/u012563853/article/details/135095387
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。