C#中DataGridView多層表頭的制作(2)
1.引言:
以前也做過一下多表頭,可是還不能使用.
http://blog.csdn.net/manimanihome/archive/2008/01/22/2058514.aspx
2.原理:
利用數組保存表頭信息,數組的格式有一定的限制,然后用CellPainting事件來重繪表頭.
3.實現:
本文在使用時,最好將ColumnHeadersHeightSizeMode設為DisableResizing,因為如果設成AutoSize,對于跨多列的表頭,在拖動分隔條調整列大小時,表頭文字顯示有重复或不完整的現象,需點擊一下表頭才顯示正常.
--1.首先做一個表頭類
MultiHead.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace CellPaintingDataGridView
{
class MultiHead
{
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 void Draw(DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex == -1)
{
using (
Brush gridBrush = new SolidBrush(this.dataGridView.GridColor),
backColorBrush = new SolidBrush(e.CellStyle.BackColor))
{
using (Pen gridLinePen = new Pen(gridBrush))
{
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
{
for (int i = 0; i < titleHead.Length; ++i)//逐行畫
{
int width;
int height;
int locationX;
int locationY;
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;
}
startColIndex--;
}
else
{
break;
}
}
//結束列
int endColIndex = e.ColumnIndex;
while (endColIndex < this.dataGridView.Columns.Count - 1)
{
if (currRow[e.ColumnIndex] == currRow[endColIndex + 1])
{
if (this.dataGridView.Columns[startColIndex + 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 - width;
locationY = e.CellBounds.Bottom - (titleHead.Length - endRowIndex - 1) * e.CellBounds.Height / titleHead.Length - height;
Rectangle recCell = new Rectangle(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;
e.Graphics.DrawString(currRow[e.ColumnIndex], e.CellStyle.Font, Brushes.Black, recCell, sf);
}
}
e.Handled = true;
}
}
}
}
}
}
--2.調用有二种方式,一种是直接在需要使用多行表頭的地方添加一個CellPainting事件(如示例1),也可以先重載一個DataGridView,這樣在設計時就可以看到效果(如示例2).
注意:最好將ColumnHeadersHeightSizeMode設為DisableResizing
示例1:
string[] title = new string[]{"column,column,column,column3,column5",
"colum1,colum1,成本,column3,column5",
"colum1,colum2,成本,column,column5"
};
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
MultiHead m = new MultiHead(this.dataGridView1, title);
m.Draw(e);
}
示例2:
DataGridViewEx.cs
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
namespace CellPaintingDataGridView
{
public partial class DataGridViewEx : DataGridView
{
public DataGridViewEx()
{
InitializeComponent();
}
public DataGridViewEx(IContainer container)
{
container.Add(this);
InitializeComponent();
}
private string[] titleHead;
public string[] TitleHead
{
get
{
return this.titleHead;
}
set
{
this.titleHead = value;
}
}
protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
{
MultiHead m = new MultiHead(this, titleHead);
m.Draw(e);
}
}
}
4.思考
其實做多表頭,用tree來做更合适一些,但沒有想到較滿意的做法,主要是支持設計時的可視化.以下是一個用tree來做的多表頭,雖然有點不規范,但還算實現了效果,可以參考.
(VB實現) http://blog.csdn.net/JoMuncher/archive/2007/11/02/1862977.aspx
(C#實現) http://blog.csdn.net/teacatcn/archive/2008/01/23/2060482.aspx
其實,我的想法是做一個TreeNodeCollection集合屬性,可是在實做時沒有成功,還望各位网友指教.