WPF DataGrid 复合表头 (实现表头合并,自定义表头)
功能说明: 将 DataGrid嵌套在本控件内,使用Label自定义表头,如果需要上下左右滚动 需要在控件外围添加 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;
}
}
}
WPF DataGrid 复合表头 (实现表头合并,自定义表头)的更多相关文章
- 小师妹问 easyUI mergeCells 行合并后表头和内容对不齐
公司来了一个做easyUI的妹子,恰好那妹子是和我一个学校的,有一天下班妹子在超时买东西正好巧遇,然后妹子就问了问题,随便说手机卡需要我帮忙刷机,然后就问手机买了多久, 多少钱,刚买的时候好用不,然后 ...
- WPF DataGrid自定义样式
微软的WPF DataGrid中有很多的属性和样式,你可以调整,以寻找合适的(如果你是一名设计师).下面,找到我的小抄造型的网格.它不是100%全面,但它可以让你走得很远,有一些非常有用的技巧和陷阱. ...
- QTableWidget自定义表头QHeaderView加全选复选框
1 QTableWidget自定义表头QHeaderView加全选复选框 在使用QTableWidget时需要在表头添加全选复选框,但是默认的表头无法添加复选框,只能用图片画上去一个复 ...
- vue + element ui 表格自定义表头,提供线上demo
前言:工作中用到 vue+element ui 的前端框架,需要使用自定义表头,需要使用 re.转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9710826.h ...
- ElementUI2.0组件库el-table表格组件如何自定义表头?
效果图: npm run dev 编译项目之后,报错,要使用jsx语法需要先安装编译插件 1.安装下列安装包 npm install babel-plugin-syntax-jsx --save-de ...
- element之table自定义表头
1.实现效果 2.使用render-header可以自定义表头 <el-table-column prop="date" label="日期" sorta ...
- ElementUI的Table-column_render-header自定义表头
ElementUI的Table表格,官方网站上提供了很多样式,但是在日常开发中还会碰到各种情况,显然官方提供的是不能满足需求的.那么,我们就根据自己的需求对table进行改造. 先丢出关于Table的 ...
- WPF DataGrid 自动生成行号的方法(通过修改RowHeaderTemplate的方式)
WPF中的DataGrid自动生成行号的方法有很多,这里记录了一种通过修改 RowHeaderTemplate的方式来生成行号: 方法一: xaml界面: <Window ... xmlns:l ...
- WPF DataGrid常用属性记录
WPF DataGrid常用属性记录 组件常用方法: BeginEdit:使DataGrid进入编辑状态. CancelEdit:取消DataGrid的编辑状态. CollapseRowGroup:闭 ...
随机推荐
- CF1203D2 Remove the Substring (hard version) 题解
这题初赛让我白给了6分,于是我决定回来解决一下它. 说实话,看原题题面和看CCF代码真是两种完全不同的感受…… ------------思路分析: 把$s$串删去一部分之后,会把$s$串分成两部分,当 ...
- PCIe例程理解(一)用户逻辑模块(接收)仿真分析
前言 本文从例子程序细节上(语法层面)去理解PCIe对于事物层数据的接收及解析. 参考数据手册:PG054: 例子程序有Vivado生成: 为什么将这个内容写出来? 通过写博客,可以检验自己理解了这个 ...
- e3mall商城总结12之购物车的实现、以及购物车小计问题、json406报错
说在前面的话 1.本节主要讲了e3mall购物车的实现方法,我搭建的项目和系统购物车有一些区别,因此这里需要说一下.系统搭建的项目在未登陆的情况下也可以通过cookie进行加入购物车,当用户要下单的时 ...
- 记录一次mybatis缓存和事务传播行为导致ut挂的排查过程
起因 rhea项目有两个ut一直都是挂的,之前也经过几个同事排查过,但是都没有找到解决办法,慢慢的这个问题就搁置了.因为之前负责rhea项目的同事离职,我临时接手了这个项目,刚好最近来了一个新同事在做 ...
- 基于模板特化的Lua自动绑定系统
LuaBind http://www.rasterbar.com/products/luabind.html http://blog.sina.com.cn/s/blog_646817c00100gk ...
- 开源基于lua gc管理c++对象的cocos2dx lua绑定方案
cocos2dx目前lua对应的c++对象的生命周期管理,是基于c++析构函数的,也就是生命周期可能存在不一致,比如c++对象已经释放,而lua对象还存在,如果这时候再使用,会有宕机的风险,为此我开发 ...
- Google Code Jam 2020 Round1B Blindfolded Bullseye
总结 这一题是道交互题,平时写的不多,没啥调试经验,GYM上遇到了少说交个十几发.一开始很快的想出了恰烂分的方法,但是没有着急写,果然很快就又把Test Set3的方法想到了,但是想到归想到,调了快一 ...
- 给MySQL中数据表添加字段
添加一个char字段: mysql> alter table stock add src char(20); Query OK, 3766 rows affected (0.65 sec) Re ...
- RocketMQ生产部署架构如何设计
前言 看了我们之前的文章,相信小伙伴们对RocketMQ已经有了一个初步的了解,那么今天我们就来聊一聊具体如何来设计一套高可用的生产部署架构. 在聊如何设计这套架构的同时,我们再补充一些之前没提到的知 ...
- 初次使用maven创建web工程发现只有一个idea目录,src,webapp目录都不见了,解决方案
修bug系列2之 初次使用maven创建web项目的src目录不知所踪 窗外下着下雨,屋内的我学着maven,本以为轻轻松松,没想到还是遇到了bug.好了不说了,来看看我是怎么解决的. 在初次使用ma ...