WPF DataGrid 复合表头控件实现,不需要写模板 (实现表头合并,自定义表头)


 

 

功能说明: 将 DataGrid嵌套在本控件内,使用Label自定义表头,避免写HeaderTemplete模板,如果需要上下左右滚动 需要在控件外围添加  ScrollViewer 并且设置  ScrollVisibility 为Auto

实现方式:控件继承了Grid,所以datagrid上面的子节点Label可以设置行列合并  以实现我们需要的 复合表头需求,  复合表头会读取 datagrid中的列,Label的宽度会跟随 datagrid表头宽度的变化,我们可以自己设置显示或者隐藏datagrid的表头,自己设置label的样式

截图:

 

使用Demo:

<UserControl x:Class="SelfControlWPF.UserControl2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SelfControlWPF.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style x:Key="h1" TargetType="DataGridColumnHeader">
<Setter Property="Foreground" Value="Blue"></Setter>
</Style>
<Style x:Key="comlab" TargetType="Label">
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
</Style>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="复合表头:" FontSize="20" Margin="5"></TextBlock>
  <ScrollViewer HorizontalScrollBarVisibility="Auto">
      <local:ComplexDataGridHeader x:Name="p" AddRow="2" >
        <Label Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Style="{StaticResource comlab}" BorderThickness="1" Content="一"></Label>
        <Label Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" Style="{StaticResource comlab}" BorderThickness="1" Content="二"></Label>
        <Label Grid.Row="0" Grid.Column="5" Grid.ColumnSpan="4" Style="{StaticResource comlab}" BorderThickness="1" Content="三"></Label>
        <Label Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="7" Style="{StaticResource comlab}" BorderThickness="1" Content="四"></Label>
        <DataGrid HeadersVisibility="All" x:Name="a" >
          <DataGrid.Columns>
            <DataGridTextColumn HeaderStyle="{StaticResource h1}" Header="A" Width="200" Binding="{Binding A}"></DataGridTextColumn>
             <DataGridTextColumn Header="B" Binding="{Binding B}" ></DataGridTextColumn>
             <DataGridTextColumn Header="C" Binding="{Binding B}" ></DataGridTextColumn>
             <DataGridTextColumn Header="D" Binding="{Binding B}" ></DataGridTextColumn>
             <DataGridTextColumn Header="E" Binding="{Binding B}" ></DataGridTextColumn>
             <DataGridTextColumn Width="1000" Header="F" Binding="{Binding B}" ></DataGridTextColumn>
             <DataGridTextColumn Header="G" Binding="{Binding B}" ></DataGridTextColumn>
             <DataGridTextColumn Header="G" Binding="{Binding B}" ></DataGridTextColumn>
             <DataGridTextColumn Header="G" Binding="{Binding B}" ></DataGridTextColumn>
           </DataGrid.Columns>
          </DataGrid>
      </local:ComplexDataGridHeader>
  </ScrollViewer>
</StackPanel>
</Grid>
</UserControl>

 

 

控件代码:

 

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace SelfControlWPF.Controls
{
    /// <summary>
    /// 将DataGrid放入控件内,用Label基于Grid的方式画复杂表头,表头合并
    /// 需要滚动的的情况下可在控件外包一层 ScrollView 设置Scrollvibility为Auto 固定高度宽度
    /// </summary>
    public class ComplexDataGridHeader : Grid
    {
        #region 依赖属性
        /// <summary>
        /// 表头需要添加的行数
        /// </summary>
        public static readonly DependencyProperty AddRowProperty =
        DependencyProperty.Register("AddRow", typeof(int), typeof(ComplexDataGridHeader));
        public int AddRow
        {
            get { return (int)GetValue(AddRowProperty); }
            set { SetValue(AddRowProperty, value); }
        }

        private double RowHeaderWidth { get; set; }
        #endregion

        #region 所有字段
        /// <summary>
        /// 需要显示的DataGrid
        /// </summary>
        private DataGrid dataGrid;

        /// <summary>
        /// 所有列宽度字典
        /// </summary>
        private readonly Dictionary<int, double> colArr = new Dictionary<int, double>();

        /// <summary>
        /// 最右边的一个Label
        /// </summary>
        private Label RightLabel;

        /// <summary>
        /// 最右边Label右边的BorderThinkness
        /// </summary>
        private double RightTh;

        /// <summary>
        /// 监测datagrid Columun的宽度变化
        /// </summary>
        readonly DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.WidthProperty, typeof(DataGridColumn));

        /// <summary>
        /// DataGrid所有Column字典
        /// </summary>
        readonly Dictionary<int, ColumnDefinition> columnDefinitions = new Dictionary<int, ColumnDefinition>();
        //double SumWidth = 0;

        /// <summary>
        /// 记录DataGrid的RowHeaderActualWidth
        /// </summary>
        #endregion

        #region 主要方法
        public ComplexDataGridHeader()
        {
            RowDefinition rowDefinitionh = new RowDefinition
            {
                Height = GridLength.Auto
            };
            RowDefinitions.Add(rowDefinitionh);
        }
       
        /// <summary>
        /// 设置DataGrid初始样式
        /// </summary>
        /// <param name="obj"></param>
        private void SetHeaderGrid(object obj)
        {
            if (obj is DataGrid)
            {
                dataGrid = obj as DataGrid;
                dataGrid.SetValue(RowProperty, 1);
                dataGrid.Loaded += DataGridLoaded;
                dataGrid.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                dataGrid.CanUserReorderColumns = false;
            }
        }
  
        /// <summary>
        /// 开始渲染表头
        /// </summary>
        public void Render()
        {
            if (Utils.ListIsNullOrEmpty(dataGrid.Columns)|| Utils.ListIsNullOrEmpty(Children)) return;
            columnDefinitions.Clear();
            colArr.Clear();
            //SumWidth = 0;
            ColumnDefinitions.Clear();
            RowDefinitions.Clear();
            RowDefinition rowDefinitionh = new RowDefinition
            {
                Height = GridLength.Auto
            };
            RowDefinitions.Add(rowDefinitionh);
            foreach (DataGridColumn col in dataGrid.Columns)
            {
                descriptor.RemoveValueChanged(col, ColumnWidth_Changed);
                descriptor.AddValueChanged(col, ColumnWidth_Changed);
                ColumnDefinition columnDefinition = new ColumnDefinition
                {
                    Width = new GridLength(col.ActualWidth >= col.Width.Value ? col.ActualWidth : col.Width.Value)
                };
                ColumnDefinitions.Add(columnDefinition);
                colArr.Add(col.DisplayIndex, columnDefinition.Width.Value);
                columnDefinitions.Add(col.DisplayIndex, columnDefinition);
                //SumWidth += columnDefinition.Width.Value;
            }

            if(dataGrid.HeadersVisibility== DataGridHeadersVisibility.All|| dataGrid.HeadersVisibility == DataGridHeadersVisibility.Row)
            {
                RowHeaderWidth+= dataGrid.RowHeaderActualWidth; 
            }
            //SumWidth += RowHeaderWidth;
            columnDefinitions[0].Width = new GridLength(columnDefinitions[0].Width.Value + RowHeaderWidth);
            colArr[0] = columnDefinitions[0].Width.Value;

            for (int i = 0; i < AddRow; i++)
            {
                RowDefinition rowDefinition = new RowDefinition
                {
                    Height = GridLength.Auto
                };
                RowDefinitions.Add(rowDefinition);
            }
            SetHeaderWidth();
            dataGrid.SetValue(ColumnSpanProperty, dataGrid.Columns.Count);
            dataGrid.SetValue(RowProperty, RowDefinitions.Count - 1);
        }

        /// <summary>
        /// 设置所有表头宽度和边框
        /// </summary>
        private void SetHeaderWidth()
        {

            foreach (var item in Children)
            {
                if (item is Label)
                {
                    Label textBlock = item as Label;
                    Thickness thickness = textBlock.BorderThickness;
                    thickness.Left = thickness.Left == 0 ? 1 : thickness.Left;
                    thickness.Top = thickness.Top == 0 ? 1 : thickness.Top;
                    thickness.Right = thickness.Right == 0 ? 1 : thickness.Right;
                    thickness.Bottom = thickness.Bottom == 0 ? 1 : thickness.Bottom;
                    double Th = thickness.Right;
                    int col = Convert.ToInt32(textBlock.GetValue(ColumnProperty));
                    int row = Convert.ToInt32(textBlock.GetValue(RowProperty));
                    int colspan = Convert.ToInt32(textBlock.GetValue(ColumnSpanProperty));
                    textBlock.Width = GetLabelWidth(Convert.ToInt32(textBlock.GetValue(ColumnProperty)), Convert.ToInt32(textBlock.GetValue(ColumnSpanProperty)));
                    textBlock.BorderThickness = new Thickness(thickness.Left, thickness.Top * (row == 0 ? 1 : 0), thickness.Right * (col + colspan - 1 == dataGrid.Columns.Count - 1 ? 1 : 0), thickness.Bottom * (row == RowDefinitions.Count - 2 ? 0 : 1));
                    if (RightLabel is null || Convert.ToInt32(textBlock.GetValue(ColumnProperty)) > Convert.ToInt32(RightLabel.GetValue(ColumnProperty)))
                    {
                        RightLabel = textBlock;
                        if (Th > 0) RightTh = thickness.Right;

                    }
                }
            }
            RightLabel.BorderThickness = new Thickness(RightLabel.BorderThickness.Left, RightLabel.BorderThickness.Top, RightTh, RightLabel.BorderThickness.Bottom);
        }

        /// <summary>
        /// 计算某个表头宽度
        /// </summary>
        /// <param name="col"></param>
        /// <param name="span"></param>
        /// <returns></returns>
        private double GetLabelWidth(int col, int span)
        {
            double result = 0;
            for (int i = 0; i < span; i++)
            {
                if(colArr.ContainsKey(col + i))
                result += colArr[col + i];
            }
            return result;
        }

        #endregion

        #region 事件

        /// <summary>
        /// 监测控件子控件变化
        /// </summary>
        /// <param name="visualAdded"></param>
        /// <param name="visualRemoved"></param>
        protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
        {
            if (visualAdded is DataGrid)
            {
                SetHeaderGrid(visualAdded as DataGrid);
            }
            base.OnVisualChildrenChanged(visualAdded, visualRemoved);



        }

        /// <summary>
        /// 表头宽度变化事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ColumnWidth_Changed(object sender, EventArgs e)
        {
            DataGridColumn dataGridColumn = sender as DataGridColumn;
            columnDefinitions[dataGridColumn.DisplayIndex].Width = new GridLength(dataGridColumn.ActualWidth);
            if (!colArr.ContainsKey(dataGridColumn.DisplayIndex)) return;
            colArr[dataGridColumn.DisplayIndex] = dataGridColumn.ActualWidth;
            if (dataGridColumn.DisplayIndex == 0)
            {
                columnDefinitions[0].Width = new GridLength(dataGridColumn.ActualWidth + RowHeaderWidth);
                colArr[0] = columnDefinitions[0].Width.Value;
            }
            SetHeaderWidth();
        }

        /// <summary>
        /// DataGrid加载后事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataGridLoaded(object sender, RoutedEventArgs e)
        {
            Render();
        }

        #endregion
    }
}

帮助类:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;
using System.Xml;

namespace SelfControlWPF
{
    public class Utils
    {
        /// <summary>
        /// 深拷贝控件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        public static void Copy<T>(T source, ref T destination) where T : class
        {
                string rectXaml = XamlWriter.Save(source);
                StringReader stringReader = new StringReader(rectXaml);
                XmlReader xmlReader = XmlReader.Create(stringReader);
                UIElement clonedChild = (UIElement)XamlReader.Load(xmlReader);
                destination = clonedChild as T;
        }

        /// <summary>
        /// 判断列表是否为空或者0条
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static bool ListIsNullOrEmpty(IList list)
        {
            return list == null || list.Count == 0;
        }
    }
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM