Winform 实现DataGridView的单元格(DataGridViewCell)中添加多个控件效果

92 阅读4分钟

实现原因

        因为现有的DataGridView控件无法支持在单元格DataGridViewCell上显示多个甚至是多种类型的控件

实现效果

        效果1

52a6e20d1105461dbb4be7014f05a4fa.png

        效果2

5cbeb2b54f684de38e6082f63d67afaa.gif

实现代码

核心代码实现

using System;
using System.Drawing;
using System.Windows.Forms;
 
namespace DataGridViewCellExtLib
{
    /// <summary>
    /// DataGridViewCell扩展类
    /// </summary>
    public static class DataGridViewCellExtension
    {
        /// <summary>
        /// 添加Panel控件到DataGridView
        /// </summary>
        /// <param name="cell">DataGridViewCell对象</param>
        /// <param name="dataGridView">DataGridView对象</param>
        /// <returns>返回已指定好大小,位置,添加到DataGridView控件的Panel对象</returns>
        /// <exception cref="ArgumentNullException">
        /// 如果cell为Null,抛出ArgumentNullException异常
        /// 如果dataGridView为Null,抛出ArgumentNullException异常
        /// </exception>
        public static Panel AddPanelControl(this DataGridViewCell cell, DataGridView dataGridView)
        {
            if (cell == null)
            {
                throw new ArgumentNullException(nameof(cell));
            }
 
            if (dataGridView == null)
            {
                throw new ArgumentNullException(nameof(dataGridView));
            }
 
            var panel = new Panel
            {
                //默认Panel颜色为透明
                BackColor = Color.Transparent
            };
 
            //添加控件到DataGridView
            dataGridView.Controls.Add(panel);
 
            //获取单元格的矩形范围
            Rectangle rectangle = dataGridView.GetCellDisplayRectangle(cell.ColumnIndex, cell.RowIndex, false);
 
            //设置Panel的大小
            panel.Size = new Size()
            {
                Width = rectangle.Width,
                Height = rectangle.Height
            };
 
            //显示Panel
            panel.Visible = true;
            //设置Panel位置
            panel.Location = new Point(rectangle.Left, rectangle.Top);
 
            //将Panel和DataGridViewCell绑定在一起
            panel.Tag = new TagPlus<DataGridViewCell>
            {
                Binding = cell
            };
            cell.Tag = new TagPlus<Panel>
            {
                Binding = panel
            };
 
            return panel;
        }
 
        /// <summary>
        /// 从DataGridView中移除Panel控件
        /// </summary>
        /// <param name="cell">DataGridViewCell对象</param>
        /// <param name="dataGridView">DataGridView对象</param>
        /// <exception cref="ArgumentNullException">
        /// 如果cell为Null,抛出ArgumentNullException异常
        /// 如果dataGridView为Null,抛出ArgumentNullException异常
        /// </exception>
        public static void RemovePanelControl(this DataGridViewCell cell, DataGridView dataGridView)
        {
            if (cell == null)
            {
                throw new ArgumentNullException(nameof(cell));
            }
 
            if (dataGridView == null)
            {
                throw new ArgumentNullException(nameof(dataGridView));
            }
 
            var panel = ((TagPlus<Panel>)cell.Tag).Binding;
 
            //移除Panel控件
            dataGridView.Controls.Remove(panel);
            panel.Dispose();
        }
 
        /// <summary>
        /// 重新在DataGridView控件中设置Panel位置
        /// </summary>
        /// <param name="cell">DataGridViewCell对象</param>
        /// <param name="dataGridView">DataGridView对象</param>
        /// <exception cref="ArgumentNullException">
        /// 如果cell为Null,抛出ArgumentNullException异常
        /// 如果dataGridView为Null,抛出ArgumentNullException异常
        /// </exception>
        public static void ResetPanelControlLocation(this DataGridViewCell cell, DataGridView dataGridView)
        {
            if (cell == null)
            {
                throw new ArgumentNullException(nameof(cell));
            }
 
            if (dataGridView == null)
            {
                throw new ArgumentNullException(nameof(dataGridView));
            }
 
            var tagPlus = (TagPlus<Panel>)cell.Tag;
            var panel = tagPlus.Binding;
 
            //获取单元格的矩形范围
            Rectangle rectangle = dataGridView.GetCellDisplayRectangle(cell.ColumnIndex, cell.RowIndex, false);
            //设置Panel位置
            panel.Location = new Point(rectangle.Left, rectangle.Top);
        }
    }
 
    /// <summary>
    /// Tag扩展类
    /// </summary>
    /// <typeparam name="T">需要绑定的数据类型</typeparam>
    public class TagPlus<T>
    {
        /// <summary>
        /// 需要绑定的元素
        /// </summary>
        public T Binding { get; set; }
 
        /// <summary>
        /// 将Control.Tag属性保留到当前属性上
        /// </summary>
        public object Tag { get; set; }
    }
}

程序代码地址下载

gitee.com/GandalfGao/…

设计思路

1. 为什么以DataGridViewCell的扩展类方式实现

从界面的实现角度来看,控件是添加到单元格中的(实际却不是这样的,添加显示的控件实际和DataGridViewCell一点关系都没有,它们也没有父级和子级的关系,添加的控件只和DataGridView控件有父级和子级的关系)。因此,我想设计一个这样的扩展方法,使其开发起来就好像是直接对DataGridViewCell操作一样, 比如添加控件,虽然实际是往DataGridView控件上做添加,但是开发者在用扩展方法时,就仿佛是在对DataGridViewCell做添加一样,这样的话在感官上显得更加直接

2. 为什么添加到DataGridView的都是只是一个Panel控件

        a. 是为了化繁为简

   我需要设计一个root的控件,用这个控件来承接多个甚至是多个不同种类的子控件。这样的设计的好处有两点:

   第一:对于DataGridViewCell来说,只需要面对这一个root控件即可,它不需要再关注root控件里面究竟设计了什么样的控件,它们的位置是在哪里。它只需要对一个控件做好负责,那就是这个root控件,只要设计好这个root的控件大小和其位置就可以了,大小是单元格的大小,位置是单元格的位置

   第二:设计这个root控件之后,子控件的位置就是相对root控件位置来设置了,不需要再相对DataGridView控件位置来设置。

        b. 为什么选择Panel做为这个root控件

   因为子控件在Panel控件中布局更加简单和自由

3. TagPlus类是做什么用的

   简单来说,Control类中带有Tag属性,TagPlus可以理解为是Tag属性的扩展版

        那为什么需要这个类来做为Tag的扩展呢?

   这就得提到我其中的一个设计思路。由于DatGridViewCell控件和Panel控件之间没有关联关系,而是DataGridView控件和Panel有关联关系,且是一对多的关系。所以我就想通过某种方式将DataGridViewCell控件和Panel控件绑定在一起,让它们之间有一定关联关系,且是一对一的关联关系,首先想到的就是用Control的Tag属性,但是担心Tag属性被占用后,如果想要添加别的数据到Tag上那就无法赋值了,所以我加了TagPlus类,在提供绑定控件属性的同时,且也保留了Tag属性。绑定之后呢,DataGridViewCell和Panel就有了关联关系,DataGridViewCell就成了Panel逻辑上的“父控件”,Panel成了DataGridViewCell逻辑上的“子控件”,这样也更好的可以通过DataGridViewCell去访问到Panel控件,通过Panel控件更好的访问到DataGridViewCell控件。

结尾

如果您觉得我还写的不错,请给我一点点的鼓励,您的鼓励就是我坚持的动力,当然啦,一切要量力而行,不要勉强哦!

127eac18dcbc4305afaa813a51b167ee.jpeg