C#自定义组件实现表格的多层表头功能

 更新时间:2024年12月13日 10:22:18   作者:数据的世界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#多层表头内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#批量修改文件后缀的实现方法

    C#批量修改文件后缀的实现方法

    这篇文章主要介绍了C#批量修改文件后缀的实现方法,本文介绍的方法是通用型的,你只要修改读取的文件地址,想要读取的文件后缀名,以及自己想更换的后缀名称即可,感兴趣的小伙伴跟着小伙伴一起来看看吧
    2024-07-07
  • C#打印日志的方法总结

    C#打印日志的方法总结

    在本篇文章里小编给大家整理了关于C#如何打印日志的技巧总结,需要的朋友们跟着学习下。
    2019-03-03
  • 100行C#代码实现经典扫雷游戏

    100行C#代码实现经典扫雷游戏

    这篇文章主要为大家详细介绍了如何用100行C#代码实现经典的扫雷游戏,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-02-02
  • C#将Word转换成PDF方法汇总(基于Office和WPS)

    C#将Word转换成PDF方法汇总(基于Office和WPS)

    这篇文章主要汇总了C#将Word转换成PDF方法,基于Office和WPS的两种解决方案,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • WPF利用CommunityToolkit.Mvvm实现级联选择器

    WPF利用CommunityToolkit.Mvvm实现级联选择器

    这篇文章主要介绍了WPF如何利用CommunityToolkit.Mvvm实现级联选择器,文中的示例代码讲解详细,对我们的学习或工作有一定帮助,需要的小伙伴可以参考一下
    2023-12-12
  • C#获取ListView鼠标下的Item实例

    C#获取ListView鼠标下的Item实例

    下面小编就为大家带来一篇C#获取ListView鼠标下的Item实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Unity中C#和Java的相互调用实例代码

    Unity中C#和Java的相互调用实例代码

    在unity中接入sdk或者定制一些功能时,需要调用系统接口。安卓手机实际操作中,也就是Unity与android相互调用。我们在Unity中使用c#,android中使用java。
    2018-02-02
  • C#使用泛型队列Queue实现生产消费模式

    C#使用泛型队列Queue实现生产消费模式

    这篇文章介绍了C#使用泛型队列Queue实现生产消费模式的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • C#实现装箱与拆箱操作简单实例

    C#实现装箱与拆箱操作简单实例

    这篇文章主要介绍了C#实现装箱与拆箱操作,对于新手理解装箱与拆箱有一定的帮助,需要的朋友可以参考下
    2014-07-07
  • C#中Winform窗体Form的关闭按钮变灰色的方法

    C#中Winform窗体Form的关闭按钮变灰色的方法

    这篇文章主要介绍了C#中Winform窗体Form的关闭按钮变灰色的方法,对于C#程序界面的设计有一定的借鉴价值,需要的朋友可以参考下
    2014-08-08

最新评论