winforms实现单元格合并的DatagridView

ps:目前只能将所在的列 垂直合并 不能合并两(多)个不相同的列

相关参数

属性名称作用描述
NeedMergeColumns需要合并列将合并的列名写入
MergeColumnsReadOnly合并列是否只读默认【否】
MergeConditonDict合并列的条件字典默认【Null】示例:new Dictionary<string, List>{{ BillDate, new List { BillCode,BillNumber} },}; 意味发票时间 以发票代码+发票号码 两列 以及 发票时间 作为唯一标识,如果重复即合并当前列
NeedColumnCheckBox需要选择列(可做全选)?默认【true】
ColumnCheckBoxTitle选择列的标题内容默认【选择】
ColumnCheckBoxName选择列的Name值默认【select_check】
RowCheckBoxDataPropName选择列的DataPropName默认[D_Selected]
ColumnCheckBoxInLast选择列的位置是否是最后一列默认[false]为false时是第一列展示,如果是true以最后一列展示,如果AutoGenerateColumns为true时,ColumnCheckBoxInLast为true,可能不会在最后一列展示,为false则没有任何影响
ShowUploadInfoColumnsDict需要展示上传信息的列默认【Null】这个是我需要结合业务做的上传列的标记而已,不一定对你有作用,可以忽略

使用示范

//设置合并列
groupByDataGrid1.NeedMergeColumns = new string[] { BillCode.Name, BillNumber.Name, BillDate.Name, BillAmount.Name, Bill_Attach.Name };
//设置合并单元格只读
groupByDataGrid1.MergeColumnsReadOnly = true;
//设置对应的合并列的合并条件 一般是前面是合并列  后面是组合起来的唯一标识 
//如这个 { BillNumber.Name, new List<string> { BillCode.Name}},
//意味着 BillNumber+BillCode 唯一 如果重复,会将BillNumber列合并
//如果不写合并条件  该列一旦重复即合并
groupByDataGrid1.MergeConditonDict = new Dictionary<string, List<string>>
{
	{ BillNumber.Name, new List<string> { BillCode.Name}},
	{ BillDate.Name, new List<string> { BillCode.Name,BillNumber.Name } },
	{ BillAmount.Name, new List<string> { BillCode.Name, BillNumber.Name}},
	{ Bill_Attach.Name, new List<string> { BillCode.Name, BillNumber.Name}}
};
//groupByDataGrid1.ShowUploadInfoColumns = new string[] { Bill_Attach.Name };
IList<OsZbBillInfo> osZbBillInfos = new List<OsZbBillInfo>(){
	//此次数量模拟数据
};
groupByDataGrid1.DataSource = osZbBillInfos;

代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace WinformsUserControl.Controls
{
	/// <summary>
	/// 绘制单元格垂直合并的DataGridView
	/// </summary>
	public class GroupByDataGrid : DataGridView
	{

		private string _columnCheckBoxTitle = "选择";
		private string _columnCheckBoxName = "select_check";
		private string _columnCheckBoxDataPropName = "D_Selected";
		private bool _columnCheckBoxInLast = false;
		private bool _needColumnCheckbox = true;

		/// <summary>
		/// 需要绘制单元格
		/// </summary>
		//private bool _needPaintCell = true;

		/// <summary>
		/// 需要合并的列
		/// </summary>
		[Browsable(true)]
		[Description("需要合并的列")]
		public string[] NeedMergeColumns { get; set; }

		/// <summary>
		/// 合并列是否只读
		/// </summary
		[Browsable(true)]
		[Description("合并列是否只读")]
		public bool MergeColumnsReadOnly { get; set; }


		/// <summary>
		/// 需要某几项值相同 前面才合并
		/// </summary>
		[Browsable(false)]
		[Description("需要某几项值相同 前面才合并")]
		public Dictionary<string,List<string>> MergeConditonDict { get; set; }


		/// <summary>
		/// 需要展示上传信息的列 字典
		/// </summary>
		public Dictionary<string,string> ShowUploadInfoColumnsDict = new Dictionary<string, string> ();


		private string[] _showUploadInfoColumns;

		/// <summary>
		/// 需要展示上传信息的列 
		/// 在控件中无意义  做个标记而已
		/// </summary>
		[Browsable(false)]
		[Description("需要展示上传信息的列")]
		public string[] ShowUploadInfoColumns{ private get => _showUploadInfoColumns; set {
				_showUploadInfoColumns = value;
				foreach (var item in value)
				{
					if (!ShowUploadInfoColumnsDict.ContainsKey(item))
					{
						ShowUploadInfoColumnsDict.Add(item, item);
					}
				}
			}
		}

		/// <summary>
		/// 选择列
		/// </summary>
		private DataGridViewCheckBoxColumn rowCheckBoxColumn = new DataGridViewCheckBoxColumn()
		{
			AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
			Width = 51,
			FalseValue = false,
			TrueValue = true
		};

		/// <summary>
		/// 是否需要行选择框
		/// </summary>
		[Browsable(true)]
		[Description("是否需要行选择框")]
		public bool NeedColumnCheckBox { get => _needColumnCheckbox; set{
				_needColumnCheckbox = value;
				int insertIndex = ColumnCheckBoxInLast ? this.Columns.Count - 1 : 0;
				rowCheckBoxColumn.DisplayIndex = insertIndex;
				rowCheckBoxColumn.Visible = value;
				if (!Columns.Contains(rowCheckBoxColumn))
				{
					rowCheckBoxColumn.Name = ColumnCheckBoxName;
					rowCheckBoxColumn.DataPropertyName = RowCheckBoxDataPropName;
					rowCheckBoxColumn.HeaderText = ColumnCheckBoxTitle;
					this.Columns.Insert(insertIndex, rowCheckBoxColumn);
				}
			}
		}

		/// <summary>
		/// 选择框(列)标题
		/// </summary>
		[Browsable(true)]
		[Description("行选择框标题")]
		public string ColumnCheckBoxTitle { get => _columnCheckBoxTitle; set {
				_columnCheckBoxTitle = value;
				rowCheckBoxColumn.HeaderText = value;
			}
		}

		/// <summary>
		/// 选择框(列)标题
		/// </summary>
		[Browsable(true)]
		[Description("行选择框Name")]
		public string ColumnCheckBoxName { get => _columnCheckBoxName; set {
				_columnCheckBoxName = value;
				rowCheckBoxColumn.Name = value;
			} 
		}

		/// <summary>
		/// 行选择框数据属性名
		/// </summary>
		[Browsable(true)]
		[Description("行选择框数据属性名")]
		public string RowCheckBoxDataPropName { get => _columnCheckBoxDataPropName; set {
				_columnCheckBoxDataPropName = value;
				rowCheckBoxColumn.DataPropertyName = value;
			}
		}

		/// <summary>
		/// 选择框(列)是否位于最后一列,默认是第一列
		/// </summary>
		[Browsable(true)]
		[Description("行选择框是否位于最后一列,默认是第一列")]
		public bool ColumnCheckBoxInLast { get => _columnCheckBoxInLast; set {
				_columnCheckBoxInLast = value;
				int insertIndex = value ? this.Columns.Count - 1 : 0;
				rowCheckBoxColumn.DisplayIndex = insertIndex;
			}
		}



		public GroupByDataGrid()
		{
			#region 默认样式

			this.BorderStyle = BorderStyle.Fixed3D;
			this.BackgroundColor = Color.FromArgb(243, 244, 247);
			this.ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle()
			{
				ForeColor = SystemColors.WindowText,
				SelectionBackColor = SystemColors.Highlight,
				SelectionForeColor = SystemColors.HighlightText,
				Font = new Font("宋体", 9, FontStyle.Regular, GraphicsUnit.Point, 134, false),
				WrapMode = DataGridViewTriState.True,
				Alignment = DataGridViewContentAlignment.MiddleCenter
			};
			this.DefaultCellStyle = new DataGridViewCellStyle()
			{

				BackColor = SystemColors.Window,
				ForeColor = SystemColors.ControlText,
				SelectionBackColor = Color.FromArgb(255, 0, 188, 212),
				SelectionForeColor = SystemColors.HighlightText,
				Font = new Font("宋体", 9, FontStyle.Regular, GraphicsUnit.Point, 134, false),
				WrapMode = DataGridViewTriState.False,
				Alignment = DataGridViewContentAlignment.MiddleLeft
			};

			//设置公共的样式
			CommonDataGridViewRowStyle();

			this.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
			this.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
			this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;

			this.ColumnHeadersHeight = 30; // 设置表头行高为 30 像素
			this.AutoGenerateColumns = false; // 默认不自动创建列
			this.AllowUserToAddRows = false; // 默认不允许添加行
			this.AllowUserToOrderColumns = true; // 默认允许用户拖动列标头来对列重新排序

			#endregion
		}


		protected override void OnCellFormatting(DataGridViewCellFormattingEventArgs args)
		{
		
			// Call home to base
			base.OnCellFormatting(args);
			// First row always displays
			if (args.RowIndex == 0)
			{
				return;
			}
			Dictionary<string, string> mergeColDict = NeedMergeColumns?.ToDictionary(x => x, x => x);
			if (mergeColDict?.ContainsKey(this.Columns[args.ColumnIndex].Name) == true && IsRepeatedCellValue(args.RowIndex, args.ColumnIndex))
			{
				args.Value = string.Empty;
				Rows[args.RowIndex].Cells[args.ColumnIndex].ReadOnly = MergeColumnsReadOnly;
				Rows[args.RowIndex-1].Cells[args.ColumnIndex].ReadOnly = MergeColumnsReadOnly;//上一行同样设置只读性
				if(Rows[args.RowIndex].Cells[args.ColumnIndex] is DataGridViewButtonCell btnCell)
				{
					btnCell.Style.NullValue = null;  // 隐藏按钮单元格内容
					btnCell.Style.BackColor = this.DefaultCellStyle.BackColor; // 隐藏按钮单元格背景色
					btnCell.Style.SelectionBackColor = this.DefaultCellStyle.SelectionBackColor; // 隐藏按钮单元格选中背景色
				}
				Rows[args.RowIndex].Cells[args.ColumnIndex].Tag = "hidden";//标记为隐藏
				args.FormattingApplied = true;
			}
		}



		private bool IsRepeatedCellValue(int rowIndex, int colIndex)
		{
			DataGridViewCell currCell = Rows[rowIndex].Cells[colIndex];
			DataGridViewCell prevCell = Rows[rowIndex - 1].Cells[colIndex];

			string columnName = this.Columns[colIndex].Name;

			bool isMerge = true;
			if (MergeConditonDict?.TryGetValue(columnName, out var mapList)==true)
			{
				foreach (var item in mapList)
				{
					//如果有跟随合并行 重复 当前列合并 否则不合并
					if (!IsRepeatedCellValue(rowIndex, Rows[rowIndex].Cells[item].ColumnIndex))
					{
						isMerge = false;
					}
				}
			}
			if (EqualityComparer<object>.Default.Equals(currCell.Value, prevCell.Value) && isMerge)
			{
				return true;
			}
			else
			{
				return false;
			}
		}


		protected override void OnCellPainting(DataGridViewCellPaintingEventArgs args)
		{
	
			base.OnCellPainting(args);

			args.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None;
			Dictionary<string, string> mergeColDict = NeedMergeColumns?.ToDictionary(x => x, x => x);

			// Ignore column and row headers and first row
			if (args.RowIndex < 1 || args.ColumnIndex < 0)
			{
				return;
			}
			//匹配要合并的列	
			if (mergeColDict?.ContainsKey(this.Columns[args.ColumnIndex].Name) == true && IsRepeatedCellValue(args.RowIndex, args.ColumnIndex))
			{
				args.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None;

				//当前按钮单元格不绘制
				if (Rows[args.RowIndex].Cells[args.ColumnIndex] is DataGridViewButtonCell btnCell)
				{
					// 取消绘制边框
					args.AdvancedBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None;
					// 取消绘制内容
					args.PaintBackground(args.CellBounds, false);
					args.Handled = true;
				}
			}
			else
			{
				args.AdvancedBorderStyle.Top = AdvancedCellBorderStyle.Top;
			}
		}

		/// <summary>
		/// 单元格内容点击
		/// </summary>
		/// <param name="e"></param>
		/// <exception cref="ArgumentOutOfRangeException"></exception>
		protected override void OnCellContentClick(DataGridViewCellEventArgs e)
		{
			base.OnCellContentClick(e);
			if (e.RowIndex < 0 || e.ColumnIndex < 0)
			{
				return;
			}
			if (Columns[e.ColumnIndex].Name == ColumnCheckBoxName && Rows[e.RowIndex].Cells[e.ColumnIndex].Value is bool bvalue)
			{
				Rows[e.RowIndex].Cells[e.ColumnIndex].Value = !bvalue;
			}
		}

		/// <summary>
		/// 全选和反选
		/// </summary>
		/// <param name="e"></param>
		/// <exception cref="ArgumentOutOfRangeException"></exception>
		public virtual void RevertChooseAll()
		{
			foreach (DataGridViewRow row in Rows)
			{
				if (row.Cells[ColumnCheckBoxName].Value is bool bvalue)
				{
					row.Cells[ColumnCheckBoxName].Value = !bvalue;
				}
			}
		}


		/// <summary>
		/// 公共的Datagridview样式 
		/// </summary>
		/// <param name="dataGridView">需要操作的Datagridview</param>
		/// <param name="buttonStyle">是否需要操作行里面按钮样式</param>
		public  void CommonDataGridViewRowStyle(float head_fontSize = 10.5f,float content_fontSize = 11.2f,bool buttonStyle = true)
		{
			this.RowsDefaultCellStyle.ForeColor = Color.Black;
			this.RowsDefaultCellStyle.BackColor = Color.White;
			this.RowTemplate.DefaultCellStyle.SelectionBackColor = Color.FromArgb(215, 233, 246);
			this.RowTemplate.DefaultCellStyle.SelectionForeColor = Color.Black;
			if (buttonStyle)
			{
				DataGridViewColumnCollection columns = this.Columns;

				DataGridViewButtonColumn btnColumn;
				foreach (DataGridViewColumn item in columns)
				{
					//仅操作DataGridViewButtonColumn 
					if ((btnColumn = item as DataGridViewButtonColumn) != null)
					{
						btnColumn.CellTemplate.Style.BackColor = Color.FromArgb(243, 244, 247);
						btnColumn.FlatStyle = FlatStyle.Popup;
					}
				}
			}
		}

		/// <summary>
		/// 单元格内容改变
		/// </summary>
		/// <param name="e"></param>
		protected override void OnCellValueChanged(DataGridViewCellEventArgs e)
		{
            base.OnCellValueChanged(e);
			if(e.RowIndex < 0 || e.ColumnIndex < 0)
			{
				return;
			}
			// 获取改变值的单元格所在的行
			DataGridViewRow row = Rows[e.RowIndex];

			// 标记该行的所有单元格为无效
			InvalidateCell(row.Cells[e.ColumnIndex]);
			/*foreach (DataGridViewCell cell in row.Cells)
			{
				cell.DataGridView.InvalidateCell(cell);
			}*/
		}

		/// <summary>
		/// 加载完毕
		/// </summary>
		/// <param name="e"></param>
		protected override void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e)
		{
			base.OnDataBindingComplete(e);
			//单项内容改变 不重绘 
			//_needPaintCell = e.ListChangedType != ListChangedType.ItemChanged;
        }

	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员ken

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值