因工作需要使用DataGridView实现多层表头的显示,在网上找了些实现的例子(在此感谢manimanihome的博文提供原始素材,需要看原理的请转过去查看),但在绘制时的计算逻辑上有些问题,自己经过了一些修改勉强可以满足需要了。
还存在的问题有:在拖动改变列宽后有时候字/线没有刷过来;鼠标在标题栏上移动时会不停的刷影响效果。
使用时需要注意3个地方:主要类包含MultiHead,MutiHeadDGV:DataGridView;
Datagridview的ColumnHeadersHeightSizeMode属性最好设为EnableResizing;
布局的列数需要与多层列的最低级标题数一致。
下面上代码:
MultiHead类
class MultiHead
{
#region ...静态...
private static SortedList<string, int> cellPointX;
/// <summary>
/// 获取标题单元格的左边距
/// </summary>
/// <param name="rowindex"></param>
/// <param name="columnindex"></param>
/// <returns></returns>
private static int GetCellPointX(int rowindex, int columnindex)
{
string key = string.Format("{0}.{1}",rowindex,columnindex);
return cellPointX[key];
}
/// <summary>
/// 设置或更新标题单元格的左边距
/// </summary>
/// <param name="rowindex"></param>
/// <param name="columnindex"></param>
/// <param name="x"></param>
/// <returns></returns>
private static void SetCellPoint(int rowindex, int columnindex, int x)
{
if (cellPointX == null)
cellPointX = new SortedList<string, int>();
string key = string.Format("{0}.{1}", rowindex, columnindex);
if (cellPointX.ContainsKey(key))
cellPointX[key] = x;
else
cellPointX.Add(key, x);
}
#endregion
private DataGridView dataGridView;
public MultiHead(DataGridView grid)
{
this.dataGridView = grid;
string title = "";
for (int i = 0; i != this.dataGridView.Columns.Count - 1; ++i)
{
title += this.dataGridView.Columns[i].HeaderText + ",";
}
title = title.Substring(1, title.Length - 2);
this.titleHead = new string[] { title };
}
//通過构造函數來限制title的格式,始終与grid保持一致
public MultiHead(DataGridView grid, string[] title)
{
//grid不等于null
for (int i = 0; i != title.Length - 1; ++i)
{
string[] s = title[i].Split(',');
if (grid.Columns.Count == s.Length)
{
continue;
}
else
{
throw new Exception("title的元素個數与grid的欄位總數不一致.");
}
}
this.dataGridView = grid;
this.titleHead = title;
}
private string[] titleHead;
public string[] TitleHead
{
get
{
return titleHead;
}
}
public string Draw(DataGridViewCellPaintingEventArgs e)
{
string message = string.Empty;
if (e.RowIndex == -1)
{
using (
Brush gridBrush = new SolidBrush(this.dataGridView.GridColor),
backColorBrush = new SolidBrush(e.CellStyle.BackColor))
{
using (Pen gridLinePen = new Pen(gridBrush))
{
gridLinePen.Color = dataGridView.GridColor;
if (e.ColumnIndex == -1)
{
e.Graphics.FillRectangle(backColorBrush, e.CellBounds);
//畫右邊線
e.Graphics.DrawLine(gridLinePen, e.CellBounds.Right - 1, e.CellBounds.Top, e.CellBounds.Right - 1, e.CellBounds.Bottom);
//畫bottom線
e.Graphics.DrawLine(gridLinePen, e.CellBounds.Left, e.CellBounds.Bottom - 1, e.CellBounds.Right, e.CellBounds.Bottom - 1);
}
else
{
//第一行已经绘制的上一个标题
string[] drawtitles = new string[titleHead.Length];
for (int i = 0; i < titleHead.Length; ++i)//逐行畫
{
SetCellPoint(i, e.ColumnIndex,e.CellBounds.Width);
int width;
int height;
int locationX;
int locationY;
int paintingwidth=0;
string[] currRow = titleHead[i].Split(',');
width = e.CellBounds.Width;
//開始列
int startColIndex = e.ColumnIndex;
while (startColIndex > 0)
{
if (currRow[e.ColumnIndex] == currRow[startColIndex - 1])
{
if (this.dataGridView.Columns[startColIndex - 1].Visible)
{
width += this.dataGridView.Columns[startColIndex - 1].Width;
}
if (startColIndex <= e.ColumnIndex)
paintingwidth += this.dataGridView.Columns[startColIndex].Width;
startColIndex--;
}
else
{
break;
}
}
paintingwidth += GetCellPointX(i, startColIndex);
//結束列
int endColIndex = e.ColumnIndex;
while (endColIndex < this.dataGridView.Columns.Count - 1)
{
if (currRow[e.ColumnIndex] == currRow[endColIndex + 1])
{
if (this.dataGridView.Columns[endColIndex + 1].Visible)
{
width += this.dataGridView.Columns[endColIndex + 1].Width;
}
endColIndex++;
}
else
{
break;
}
}
height = e.CellBounds.Height / titleHead.Length;
//開始行
int startRowIndex = i;
while (startRowIndex > 0)
{
string[] overRow = titleHead[startRowIndex - 1].Split(',');
if (currRow[e.ColumnIndex] == overRow[e.ColumnIndex])
{
int overStartColIndex = e.ColumnIndex;
int overEndColIndex = e.ColumnIndex;
while (overStartColIndex > 0)
{
if (overRow[e.ColumnIndex] == currRow[overStartColIndex - 1])
{
overStartColIndex--;
}
else
{
break;
}
}
while (overEndColIndex < this.dataGridView.Columns.Count - 1)
{
if (overRow[e.ColumnIndex] == overRow[overEndColIndex + 1])
{
overEndColIndex++;
}
else
{
break;
}
}
if (startColIndex == overStartColIndex && endColIndex == overEndColIndex)
{
height += e.CellBounds.Height / titleHead.Length;
startRowIndex--;
}
else
{
break;
}
}
else
{
break;
}
}
//結束行
int endRowIndex = i;
while (endRowIndex < titleHead.Length - 1)
{
string[] lowRow = titleHead[endRowIndex + 1].Split(',');
if (currRow[e.ColumnIndex] == lowRow[e.ColumnIndex])
{
int lowStartColIndex = e.ColumnIndex;
int lowEndColIndex = e.ColumnIndex;
while (lowStartColIndex > 0)
{
if (lowRow[e.ColumnIndex] == currRow[lowStartColIndex - 1])
{
lowStartColIndex--;
}
else
{
break;
}
}
while (lowEndColIndex < this.dataGridView.Columns.Count - 1)
{
if (lowRow[e.ColumnIndex] == lowRow[lowEndColIndex + 1])
{
lowEndColIndex++;
}
else
{
break;
}
}
if (startColIndex == lowStartColIndex && endColIndex == lowEndColIndex)
{
height += e.CellBounds.Height / titleHead.Length;
endRowIndex++;
}
else
{
break;
}
}
else
{
break;
}
}
locationX = e.CellBounds.Right - paintingwidth;// e.CellBounds.Right - width;
locationY = e.CellBounds.Bottom - (titleHead.Length - endRowIndex - 1) * e.CellBounds.Height / titleHead.Length - height;
Rectangle recCell = new Rectangle(locationX, locationY, width, height);
message += string.Format("{0}:({1},{2},{3},{4})\r\n", currRow[e.ColumnIndex], locationX, locationY, width, height);
//erase cell
e.Graphics.FillRectangle(backColorBrush, recCell);
//畫right和bottom線
e.Graphics.DrawLine(gridLinePen, locationX + width - 1, locationY - 1, locationX + width - 1, locationY + height - 1);
e.Graphics.DrawLine(gridLinePen, locationX - 1, locationY + height - 1, locationX + width - 1, locationY + height - 1);
//畫文字
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(currRow[e.ColumnIndex], e.CellStyle.Font, Brushes.Black, recCell, sf);
}
}
e.Handled = true;
}
}
}
return message;
}
}
MutiHeadDGV:DataGridView
class MutiHeadDGV:DataGridView
{
private string[] titleHead;
public string[] TitleHead
{
get
{
return this.titleHead;
}
set
{
this.titleHead = value;
}
}
protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
{
titleHead = new string[] { "基础资料,基础资料,基础资料,基础资料,附加信息,附加信息,附加信息,附加信息,描述",
"姓名,籍贯,出生时间,性别,现住址,职业,收入,爱好,描述"};
MultiHead m = new MultiHead(this, titleHead);
}
}
TitleHead属性<strong style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">多层标题是可以在外部赋值的哦</strong>