WinForm DataGridView 组件合并单元格
我最近在用.NET Windows Forms ( WinForms ) 开发的过程中遇到了对 DataGridView 表头进行多样化合并的需求。
最早期的思路是去重绘DataGridView组件的表头部分,后来发现还是无法满足表格内部单元格合并的需求,于是决定隐藏表头,完全用其单元格来显示表格,上面的几行数据行作为表头。
对表格的进行单元格合并是在程序开发中经常遇到的需求,可惜截止目前,微软公司还没有在 .NET Framework 中自带这样的功能,开发者只能自己设法解决。
参考了网上一些内容后,思路清晰了:
用 Point 对象的XY值记录单元格的横纵序号,定义一个结构,记录哪些区域的单元格要合并。
定义一个对象,记录合并的区域的文本、起始坐标,宽度和高度信息。
首先正常输出表格,要合并的单元格里面填写相同的内容。
遍历要合并的区域信息,重新绘制表格边线,再将文本居中绘制。
在绘制边线的时候有注意到,DataGridView 自己会处理左侧和上边缘的线条,因此只需绘制下边线和和右边线。
演示合并效果如下:
具体代码如下:
/// <summary> /// 表明演示中要合并的区域开始单元格的序号坐标,X为横向,y为纵向 /// </summary> List<MergeCellInfo> lstMergeCellInfo = new List<MergeCellInfo>() { new MergeCellInfo{ PtStart = new Point(0, 0), PtEnd= new Point(1, 0) }, new MergeCellInfo{ PtStart = new Point(0, 1), PtEnd= new Point(0, 2) }, new MergeCellInfo{ PtStart = new Point(1, 3), PtEnd= new Point(2, 4) }, new MergeCellInfo{ PtStart = new Point(3, 8), PtEnd= new Point(7, 8) }, };
/// <summary> /// 用来存放要合并的内容、左上角点、宽度高度 /// </summary> public partial class SpanEntity { /// <summary> /// 显示文本 /// </summary> public virtual string Item { get; set; } /// <summary> /// 合并区域的左上角坐标 /// </summary> public virtual Point XY { get; set; } /// <summary> /// 宽度和高度 /// </summary> public virtual Size WH { get; set; } }
/// <summary> /// 要合并的区域开始单元格的起止信息,区域左角的单元格为开始,右下为结束。 /// 如要合并左上角的4个单元格合并,则new MergeCellInfo{ PtStart = new Point(0, 0), PtEnd= new Point(1, 1) } /// </summary> public class MergeCellInfo { /// <summary> /// 要合并的区域开始单元格的序号坐标,X为横向,y为纵向 /// </summary> public Point PtStart { get; set; } /// <summary> /// 要合并的区域结束单元格的序号坐标,X为横向,y为纵向 /// </summary> public Point PtEnd { get; set; } }
/// <summary> /// 绘制表格事件中对指明要合并的区域进行处理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void dgvData_Paint(object sender, PaintEventArgs e) { List<SpanEntity> PointList = new List<SpanEntity>(); for (int i = 0; i < lstMergeCellInfo.Count; i++) { if (dgvData.Rows.Count > lstMergeCellInfo[i].PtEnd.X && dgvData.Columns.Count > lstMergeCellInfo[i].PtEnd.Y) { var rectStart = dgvData.GetCellDisplayRectangle(lstMergeCellInfo[i].PtStart.Y, lstMergeCellInfo[i].PtStart.X, true); PointList.Add(new SpanEntity() { Item = dgvData.Rows[lstMergeCellInfo[i].PtStart.X].Cells[lstMergeCellInfo[i].PtStart.Y].Value.ToString(), XY = new Point(rectStart.X, rectStart.Y), WH = new Size(rectStart.Width * (lstMergeCellInfo[i].PtEnd.Y - lstMergeCellInfo[i].PtStart.Y + 1), rectStart.Height * (lstMergeCellInfo[i].PtEnd.X - lstMergeCellInfo[i].PtStart.X + 1)) }); } } //重绘合并的cell Brush backColorBrush = new SolidBrush(Color.White); Pen gridLinePen = new Pen(this.dgvData.GridColor);//线色 foreach (var obj in PointList) { e.Graphics.FillRectangle(backColorBrush, new Rectangle(obj.XY, obj.WH)); //画右边线 //右边线X点 e.Graphics.DrawLine(gridLinePen, obj.XY.X + obj.WH.Width - 1, obj.XY.Y, obj.XY.X + obj.WH.Width - 1, obj.XY.Y + obj.WH.Height - 1); //画下边线 //右边线X点 e.Graphics.DrawLine(gridLinePen, obj.XY.X, obj.XY.Y + obj.WH.Height - 1, obj.XY.X + obj.WH.Width - 1, obj.XY.Y + obj.WH.Height - 1); var sizeFont = e.Graphics.MeasureString(obj.Item, dgvData.Font); //根据第1列的字体算出value所占大小 StringFormat format = new StringFormat(); //样式 format.LineAlignment = StringAlignment.Center; // 垂直居中 format.Alignment = StringAlignment.Center; // 水平居中 RectangleF rect = new Rectangle(obj.XY, obj.WH); e.Graphics.DrawString(obj.Item, dgvData.Font, Brushes.Black, rect, format); } }
这个重新绘制的方法还不是很完美,在点击单元格或者拖动边框的时候,可能会影响效果,有待进一步完善。
留言评论