C#自定義組件實現(xiàn)表格的多層表頭功能
在 WinForms 中,想要實現(xiàn)多層表頭功能時,DataGridView 本身并不支持該功能,而且又不希望使用第三方控件,因此選擇通過自定義組件來實現(xiàn)這一需求。
首先,展示一下程序?qū)崿F(xiàn)的效果:

接下來,創(chuàng)建一個繼承自 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("設(shè)置或獲得合并表頭樹的深度")]
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">合并表頭節(jié)點</param>
///<param name="e">繪圖參數(shù)集</param>
///<param name="level">結(jié)點深度</param>
///<remarks></remarks>
public void PaintUnitHeader(TreeNode node, DataGridViewCellPaintingEventArgs e, int level)
{
//根節(jié)點時退出遞歸調(diào)用
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);
//與原貼算法有所區(qū)別在這。
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);
遞歸調(diào)用()
if (node.PrevNode == null)
if (node.Parent != null)
PaintUnitHeader(node.Parent, e, level - 1);
}
/// <summary>
/// 獲得合并標(biāo)題字段的寬度
/// </summary>
/// <param name="node">字段節(jié)點</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">底層字段節(jié)點</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("最底層結(jié)點集合")]
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">字段節(jié)點</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)
{
//行標(biāo)題不重寫
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;
}
}
}
}完成組件開發(fā)后,可以將該控件從工具箱拖入界面,表頭的節(jié)點設(shè)置通過 Designer.cs 中的 "Windows 窗體設(shè)計器生成的代碼" 實現(xiàn)。我們使用 System.Windows.Forms.TreeNode 來實現(xiàn)父子節(jié)點的綁定。示例代碼如下:
namespace CellPaintingDataGridView
{
partial class Form1
{
/// <summary>
/// 必需的設(shè)計器變量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的資源。
/// </summary>
/// <param name="disposing">如果應(yīng)釋放托管資源,為 true;否則為 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗體設(shè)計器生成的代碼
/// <summary>
/// 設(shè)計器支持所需的方法 - 不要
/// 使用代碼編輯器修改此方法的內(nèi)容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("節(jié)點0");
System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("節(jié)點1-0-0");
System.Windows.Forms.TreeNode treeNode3 = new System.Windows.Forms.TreeNode("節(jié)點1-0-1");
System.Windows.Forms.TreeNode treeNode4 = new System.Windows.Forms.TreeNode("節(jié)點1-0", new System.Windows.Forms.TreeNode[] {
treeNode2,
treeNode3});
System.Windows.Forms.TreeNode treeNode5 = new System.Windows.Forms.TreeNode("節(jié)點1-1");
System.Windows.Forms.TreeNode treeNode6 = new System.Windows.Forms.TreeNode("節(jié)點1", new System.Windows.Forms.TreeNode[] {
treeNode4,
treeNode5});
System.Windows.Forms.TreeNode treeNode7 = new System.Windows.Forms.TreeNode("節(jié)點2-0");
System.Windows.Forms.TreeNode treeNode8 = new System.Windows.Forms.TreeNode("節(jié)點2-1");
System.Windows.Forms.TreeNode treeNode9 = new System.Windows.Forms.TreeNode("節(jié)點2-0");
System.Windows.Forms.TreeNode treeNode10 = new System.Windows.Forms.TreeNode("節(jié)點2", new System.Windows.Forms.TreeNode[] {
treeNode7,
treeNode8,
treeNode9});
System.Windows.Forms.TreeNode treeNode11 = new System.Windows.Forms.TreeNode("節(jié)點3-0");
System.Windows.Forms.TreeNode treeNode12 = new System.Windows.Forms.TreeNode("節(jié)點3-1");
System.Windows.Forms.TreeNode treeNode13 = new System.Windows.Forms.TreeNode("節(jié)點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 = "節(jié)點";
treeNode1.Text = "節(jié)點0";
treeNode2.Name = "節(jié)點1-0-0";
treeNode2.Text = "節(jié)點1-0-0";
treeNode3.Name = "節(jié)點1-0-1";
treeNode3.Text = "節(jié)點1-0-1";
treeNode4.Name = "節(jié)點1-0";
treeNode4.Text = "節(jié)點1-0";
treeNode5.Name = "節(jié)點1-1";
treeNode5.Text = "節(jié)點1-1";
treeNode6.Name = "節(jié)點1";
treeNode6.Text = "節(jié)點1";
treeNode7.Name = "節(jié)點2-0";
treeNode7.Text = "節(jié)點2-0";
treeNode8.Name = "節(jié)點2-1";
treeNode8.Text = "節(jié)點2-1";
treeNode9.Name = "節(jié)點2-0";
treeNode9.Text = "節(jié)點2-0";
treeNode10.Name = "節(jié)點2";
treeNode10.Text = "節(jié)點2";
treeNode11.Name = "節(jié)點3-0";
treeNode11.Text = "節(jié)點3-0";
treeNode12.Name = "節(jié)點3-1";
treeNode12.Text = "節(jié)點3-1";
treeNode13.Name = "節(jié)點3";
treeNode13.Text = "節(jié)點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;
}
}通過這種方式,我們成功實現(xiàn)了多層表頭的功能,完全依賴自定義組件,不需要第三方控件。
到此這篇關(guān)于C#自定義組件實現(xiàn)表格的多層表頭功能的文章就介紹到這了,更多相關(guān)C#多層表頭內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# Distinct和重寫IEqualityComparer時要知道的二三事
這篇文章主要給大家介紹了關(guān)于C# Distinct和重寫IEqualityComparer時要知道的二三事,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
windows下C#定時管理器框架Task.MainForm詳解
這篇文章主要為大家詳細(xì)介紹了windows下C#定時管理器框架Task.MainForm的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
C#實現(xiàn)觀察者模式(Observer?Pattern)的兩種方式
這篇文章介紹了C#實現(xiàn)觀察者模式(Observer?Pattern)的兩種方式,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08
C#語法糖(Csharp Syntactic sugar)大匯總
首先需要聲明的是“語法糖”這個詞絕非貶義詞,它可以給我?guī)矸奖?,是一種便捷的寫法,編譯器會幫我們做轉(zhuǎn)換;而且可以提高開發(fā)編碼的效率,在性能上也不會帶來損失。這讓java開發(fā)人員羨慕不已,呵呵。2010-06-06
Unity3D運行報DllNotFoundException錯誤的解決方案
這篇文章主要介紹了Unity3D運行報DllNotFoundException錯誤的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04

