C#自定义组件实现表格的多层表头功能
作者:数据的世界01
这篇文章主要为大家详细介绍了如何使用C#自定义组件实现表格的多层表头功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
在 WinForms 中,想要实现多层表头功能时,DataGridView 本身并不支持该功能,而且又不希望使用第三方控件,因此选择通过自定义组件来实现这一需求。
首先,展示一下程序实现的效果:

接下来,创建一个继承自 DataGridView 的自定义组件。代码如下:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
namespace CellPaintingDataGridView
{
public partial class TreeHeadDataGridView : DataGridView
{
private TreeView treeView1=new TreeView();
public TreeHeadDataGridView()
{
InitializeComponent();
}
public TreeHeadDataGridView(IContainer container)
{
container.Add(this);
InitializeComponent();
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public TreeNodeCollection HeadSource
{
get { return this.treeView1.Nodes; }
}
private int _cellHeight = 17;
private int _columnDeep = 1;
[Description("设置或获得合并表头树的深度")]
public int ColumnDeep
{
get
{
if (this.Columns.Count == 0)
_columnDeep = 1;
this.ColumnHeadersHeight = _cellHeight * _columnDeep;
return _columnDeep;
}
set
{
if (value < 1)
_columnDeep = 1;
else
_columnDeep = value;
this.ColumnHeadersHeight = _cellHeight * _columnDeep;
}
}
///<summary>
///绘制合并表头
///</summary>
///<param name="node">合并表头节点</param>
///<param name="e">绘图参数集</param>
///<param name="level">结点深度</param>
///<remarks></remarks>
public void PaintUnitHeader(TreeNode node, DataGridViewCellPaintingEventArgs e, int level)
{
//根节点时退出递归调用
if (level == 0)
return;
RectangleF uhRectangle;
int uhWidth;
SolidBrush gridBrush = new SolidBrush(this.GridColor);
Pen gridLinePen = new Pen(gridBrush);
StringFormat textFormat = new StringFormat();
textFormat.Alignment = StringAlignment.Center;
uhWidth = GetUnitHeaderWidth(node);
//与原贴算法有所区别在这。
if (node.Nodes.Count == 0)
{
uhRectangle = new Rectangle(e.CellBounds.Left,
e.CellBounds.Top + node.Level * _cellHeight,
uhWidth - 1,
_cellHeight * (_columnDeep - node.Level) - 1);
}
else
{
uhRectangle = new Rectangle(
e.CellBounds.Left,
e.CellBounds.Top + node.Level * _cellHeight,
uhWidth - 1,
_cellHeight - 1);
}
Color backColor = e.CellStyle.BackColor;
if (node.BackColor != Color.Empty)
{
backColor = node.BackColor;
}
SolidBrush backColorBrush = new SolidBrush(backColor);
//画矩形
e.Graphics.FillRectangle(backColorBrush, uhRectangle);
//划底线
e.Graphics.DrawLine(gridLinePen
, uhRectangle.Left
, uhRectangle.Bottom
, uhRectangle.Right
, uhRectangle.Bottom);
//划右端线
e.Graphics.DrawLine(gridLinePen
, uhRectangle.Right
, uhRectangle.Top
, uhRectangle.Right
, uhRectangle.Bottom);
写字段文本
Color foreColor = Color.Black;
if (node.ForeColor != Color.Empty)
{
foreColor = node.ForeColor;
}
e.Graphics.DrawString(node.Text, this.Font
, new SolidBrush(foreColor)
, uhRectangle.Left + uhRectangle.Width / 2 -
e.Graphics.MeasureString(node.Text, this.Font).Width / 2 - 1
, uhRectangle.Top +
uhRectangle.Height / 2 - e.Graphics.MeasureString(node.Text, this.Font).Height / 2);
递归调用()
if (node.PrevNode == null)
if (node.Parent != null)
PaintUnitHeader(node.Parent, e, level - 1);
}
/// <summary>
/// 获得合并标题字段的宽度
/// </summary>
/// <param name="node">字段节点</param>
/// <returns>字段宽度</returns>
/// <remarks></remarks>
private int GetUnitHeaderWidth(TreeNode node)
{
//获得非最底层字段的宽度
int uhWidth = 0;
//获得最底层字段的宽度
if (node.Nodes == null)
return this.Columns[GetColumnListNodeIndex(node)].Width;
if (node.Nodes.Count == 0)
return this.Columns[GetColumnListNodeIndex(node)].Width;
for (int i = 0; i <= node.Nodes.Count - 1; i++)
{
uhWidth = uhWidth + GetUnitHeaderWidth(node.Nodes[i]);
}
return uhWidth;
}
/// <summary>
/// 获得底层字段索引
/// </summary>
/// <param name="node">底层字段节点</param>
/// <returns>索引</returns>
/// <remarks></remarks>
private int GetColumnListNodeIndex(TreeNode node)
{
for (int i = 0; i <= _columnList.Count - 1; i++)
{
if (((TreeNode)_columnList[i]).Equals(node))
return i;
}
return -1;
}
private List<TreeNode> _columnList = new List<TreeNode>();
[Description("最底層結點集合")]
public List<TreeNode> NadirColumnList
{
get
{
if (this.treeView1 == null)
return null;
if (this.treeView1.Nodes == null)
return null;
if (this.treeView1.Nodes.Count == 0)
return null;
_columnList.Clear();
foreach (TreeNode node in this.treeView1.Nodes)
{
//GetNadirColumnNodes(_columnList, node, false);
GetNadirColumnNodes(_columnList, node);
}
return _columnList;
}
}
private void GetNadirColumnNodes(List<TreeNode> alList, TreeNode node)
{
if (node.FirstNode == null)
{
alList.Add(node);
}
foreach (TreeNode n in node.Nodes)
{
GetNadirColumnNodes(alList, n);
}
}
/// <summary>
/// 获得底层字段集合
/// </summary>
/// <param name="alList">底层字段集合</param>
/// <param name="node">字段节点</param>
/// <param name="checked">向上搜索与否</param>
/// <remarks></remarks>
private void GetNadirColumnNodes(List<TreeNode> alList, TreeNode node, Boolean isChecked)
{
if (isChecked == false)
{
if (node.FirstNode == null)
{
alList.Add(node);
if (node.NextNode != null)
{
GetNadirColumnNodes(alList, node.NextNode, false);
return;
}
if (node.Parent != null)
{
GetNadirColumnNodes(alList, node.Parent, true);
return;
}
}
else
{
if (node.FirstNode != null)
{
GetNadirColumnNodes(alList, node.FirstNode, false);
return;
}
}
}
else
{
if (node.FirstNode == null)
{
return;
}
else
{
if (node.NextNode != null)
{
GetNadirColumnNodes(alList, node.NextNode, false);
return;
}
if (node.Parent != null)
{
GetNadirColumnNodes(alList, node.Parent, true);
return;
}
}
}
}
/// <summary>
/// 单元格绘制(重写)
/// </summary>
/// <param name="e"></param>
/// <remarks></remarks>
protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
{
//行标题不重写
if (e.ColumnIndex < 0)
{
base.OnCellPainting(e);
return;
}
if (_columnDeep == 1)
{
base.OnCellPainting(e);
return;
}
//绘制表头
if (e.RowIndex == -1)
{
PaintUnitHeader((TreeNode)NadirColumnList[e.ColumnIndex], e, _columnDeep);
e.Handled = true;
}
}
}
}完成组件开发后,可以将该控件从工具箱拖入界面,表头的节点设置通过 Designer.cs 中的 "Windows 窗体设计器生成的代码" 实现。我们使用 System.Windows.Forms.TreeNode 来实现父子节点的绑定。示例代码如下:
namespace CellPaintingDataGridView
{
partial class Form1
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("节点0");
System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("节点1-0-0");
System.Windows.Forms.TreeNode treeNode3 = new System.Windows.Forms.TreeNode("节点1-0-1");
System.Windows.Forms.TreeNode treeNode4 = new System.Windows.Forms.TreeNode("节点1-0", new System.Windows.Forms.TreeNode[] {
treeNode2,
treeNode3});
System.Windows.Forms.TreeNode treeNode5 = new System.Windows.Forms.TreeNode("节点1-1");
System.Windows.Forms.TreeNode treeNode6 = new System.Windows.Forms.TreeNode("节点1", new System.Windows.Forms.TreeNode[] {
treeNode4,
treeNode5});
System.Windows.Forms.TreeNode treeNode7 = new System.Windows.Forms.TreeNode("节点2-0");
System.Windows.Forms.TreeNode treeNode8 = new System.Windows.Forms.TreeNode("节点2-1");
System.Windows.Forms.TreeNode treeNode9 = new System.Windows.Forms.TreeNode("节点2-0");
System.Windows.Forms.TreeNode treeNode10 = new System.Windows.Forms.TreeNode("节点2", new System.Windows.Forms.TreeNode[] {
treeNode7,
treeNode8,
treeNode9});
System.Windows.Forms.TreeNode treeNode11 = new System.Windows.Forms.TreeNode("节点3-0");
System.Windows.Forms.TreeNode treeNode12 = new System.Windows.Forms.TreeNode("节点3-1");
System.Windows.Forms.TreeNode treeNode13 = new System.Windows.Forms.TreeNode("节点3", new System.Windows.Forms.TreeNode[] {
treeNode11,
treeNode12});
this.treeHeadDataGridView1 = new CellPaintingDataGridView.TreeHeadDataGridView(this.components);
this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column4 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column7 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column8 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column9 = new System.Windows.Forms.DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.treeHeadDataGridView1)).BeginInit();
this.SuspendLayout();
//
// treeHeadDataGridView1
//
this.treeHeadDataGridView1.ColumnDeep = 3;
this.treeHeadDataGridView1.ColumnHeadersHeight = 51;
this.treeHeadDataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
this.treeHeadDataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Column1,
this.Column2,
this.Column3,
this.Column4,
this.Column5,
this.Column6,
this.Column7,
this.Column8,
this.Column9});
treeNode1.Name = "节点";
treeNode1.Text = "节点0";
treeNode2.Name = "节点1-0-0";
treeNode2.Text = "节点1-0-0";
treeNode3.Name = "节点1-0-1";
treeNode3.Text = "节点1-0-1";
treeNode4.Name = "节点1-0";
treeNode4.Text = "节点1-0";
treeNode5.Name = "节点1-1";
treeNode5.Text = "节点1-1";
treeNode6.Name = "节点1";
treeNode6.Text = "节点1";
treeNode7.Name = "节点2-0";
treeNode7.Text = "节点2-0";
treeNode8.Name = "节点2-1";
treeNode8.Text = "节点2-1";
treeNode9.Name = "节点2-0";
treeNode9.Text = "节点2-0";
treeNode10.Name = "节点2";
treeNode10.Text = "节点2";
treeNode11.Name = "节点3-0";
treeNode11.Text = "节点3-0";
treeNode12.Name = "节点3-1";
treeNode12.Text = "节点3-1";
treeNode13.Name = "节点3";
treeNode13.Text = "节点3";
this.treeHeadDataGridView1.HeadSource.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode1,
treeNode6,
treeNode10,
treeNode13});
this.treeHeadDataGridView1.Location = new System.Drawing.Point(12, 12);
this.treeHeadDataGridView1.Name = "treeHeadDataGridView1";
this.treeHeadDataGridView1.RowTemplate.Height = 23;
this.treeHeadDataGridView1.Size = new System.Drawing.Size(1034, 456);
this.treeHeadDataGridView1.TabIndex = 0;
//
// Column1
//
this.Column1.HeaderText = "Column1";
this.Column1.Name = "Column1";
this.Column1.Width = 110;
//
// Column2
//
this.Column2.FillWeight = 80F;
this.Column2.HeaderText = "Column2";
this.Column2.Name = "Column2";
//
// Column3
//
this.Column3.FillWeight = 60F;
this.Column3.HeaderText = "Column3";
this.Column3.Name = "Column3";
this.Column3.Width = 160;
//
// Column4
//
this.Column4.HeaderText = "Column4";
this.Column4.Name = "Column4";
//
// Column5
//
this.Column5.HeaderText = "Column5";
this.Column5.Name = "Column5";
//
// Column6
//
this.Column6.HeaderText = "Column6";
this.Column6.Name = "Column6";
//
// Column7
//
this.Column7.HeaderText = "Column7";
this.Column7.Name = "Column7";
//
// Column8
//
this.Column8.HeaderText = "Column8";
this.Column8.Name = "Column8";
//
// Column9
//
this.Column9.HeaderText = "Column9";
this.Column9.Name = "Column9";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1077, 533);
this.Controls.Add(this.treeHeadDataGridView1);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.treeHeadDataGridView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private TreeHeadDataGridView treeHeadDataGridView1;
private System.Windows.Forms.DataGridViewTextBoxColumn Column1;
private System.Windows.Forms.DataGridViewTextBoxColumn Column2;
private System.Windows.Forms.DataGridViewTextBoxColumn Column3;
private System.Windows.Forms.DataGridViewTextBoxColumn Column4;
private System.Windows.Forms.DataGridViewTextBoxColumn Column5;
private System.Windows.Forms.DataGridViewTextBoxColumn Column6;
private System.Windows.Forms.DataGridViewTextBoxColumn Column7;
private System.Windows.Forms.DataGridViewTextBoxColumn Column8;
private System.Windows.Forms.DataGridViewTextBoxColumn Column9;
}
}通过这种方式,我们成功实现了多层表头的功能,完全依赖自定义组件,不需要第三方控件。
到此这篇关于C#自定义组件实现表格的多层表头功能的文章就介绍到这了,更多相关C#多层表头内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
