经常看到有同好问WINFORM里的DATAGRIDVIEW控件如何能实现双表头及合计栏,想来这是个棘手的问题,首先DATAGRIDVIEW的功能已很强大,重新写个控件费时费力,继承该控件增加一些功能自是首选。但往往做出来效果不大好,表头和合计栏看上去很“假”,不是位置就是外观让人感觉不大对劲。俺琢磨了下,用三个DATAGRIDVIEW“合并”成一个,并实现同步移动,基本可以满足要求,不注意根本看不出是三个拼起来的。
由于俺是业余编程爱好者,水平自是很菜,里面若有BUG,欢迎大家帮俺指正。
代码如下,VS2005编译通过。REBIND方法实现重新绑定、刷新数据的功能。
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Drawing;
using
System.Data;
using
System.Text;
using
System.Windows.Forms;

namespace
DataGridEx

...
{

public partial class DataGridEx : DataGridView

...{
private DataGridView FootGrid=null ;
private DataGridView DoubleGrid = null;
private bool _footvisible = false;
private bool _readonly=false ;
private bool _doublevisible = false;
private string[] FootText;
private int[] doubleGridOffset;
private int initHeight;

public bool FootVisible

...{
get

...{
return _footvisible;
}
}

public bool DoubleVisible

...{
get

...{
return _doublevisible;
}
}

public bool IsReadOnly

...{
get

...{
return _readonly;
}
set

...{
_readonly = value;
if (_readonly)

...{
foreach (DataGridViewColumn dv in this.Columns)

...{
dv.SortMode = DataGridViewColumnSortMode.NotSortable;
}
}
}
}

public DataGridEx()

...{
InitializeComponent();
}


设置DataGrid的列标题显示文字#region 设置DataGrid的列标题显示文字

/**//// <summary>
/// 设置DataGrid的列标题显示文字及输出格式。
/// </summary>
/// <param name="columnName">要绑定的表字段名</param>
/// <param name="headText">列标题数组(要和表的列数相等)</param>
/// <param name="colStyle">列样式(要和表的列数相等)</param>
/// <param name="colStyle">列宽度(要和表的列数相等)</param>
public void SetHeadStyle(string[] columnName, string[] headText, string[] colStyle,int[] columnWith)

...{
try

...{
if (columnName.Length == headText.Length && columnName.Length == colStyle.Length)

...{
DataTable dt = new DataTable();
foreach (string col in columnName)

...{
dt.Columns.Add(col);
}

this.DataSource = dt;

for (int i = 0; i < headText.Length; i++)

...{
if (headText [i]!="")

...{
this.Columns[i].HeaderText = headText[i];
}

if (colStyle[i] != "")

...{
this.Columns[i].DefaultCellStyle.Format = colStyle[i];
}

if (columnWith[i] != 0)

...{
this.Columns[i].Width = columnWith[i];
}
}


}
else

...{
throw new FormatException();
}
this.initHeight = this.Height;
}
catch (FormatException e)

...{
MessageBox.Show(e.Message+"请重新检查输入数组长度!");
}
}

public void SetHeadStyle(string[] columnName,Type[] colType, string[] headText, string[] colStyle, int[] columnWith)

...{
try

...{
if (columnName.Length == headText.Length && columnName.Length == colStyle.Length)

...{
DataTable dt = new DataTable();
for (int j = 0; j < columnName.Length; j++)

...{
dt.Columns.Add(columnName[j], colType[j]);
}
this.DataSource = dt;

for (int i = 0; i < headText.Length; i++)

...{
if (headText[i] != "")

...{
this.Columns[i].HeaderText = headText[i];
}

if (colStyle[i] != "")

...{
this.Columns[i].DefaultCellStyle.Format = colStyle[i];
}

if (columnWith[i] != 0)

...{
this.Columns[i].Width = columnWith[i];
}
}


}
else

...{
throw new FormatException();
}
this.initHeight = this.Height;
}
catch (FormatException e)

...{
MessageBox.Show(e.Message + "请重新检查输入数组长度!");
}
}
#endregion


设置合计行#region 设置合计行

public void SetFoot(string[] columnText)

...{
if (FootGrid == null)

...{
SetFootStyle();
}
this.FootText = columnText;
FootGrid.ColumnCount = this.ColumnCount;
try

...{
for (int i = 0; i < FootGrid.ColumnCount; i++)

...{
if (this.Columns[i].Visible)

...{
FootGrid.Columns[i].Width = this.Columns[i].Width;
FootGrid.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable;
if (columnText[i] != "")

...{
FootGrid.Columns[i].HeaderText = this.RowCount == 0 ? (columnText[i] == "S" ? "0" : columnText[i] + "0") :
(columnText[i] == "S" ? Convert.ToSingle((this.DataSource as DataTable).Compute("SUM(" + this.Columns[i].Name + ")", "")).ToString(this.Columns[i].DefaultCellStyle.Format) : columnText[i] + this.RowCount);
}
}
else

...{
FootGrid.Columns[i].Visible = false;
}
}
}
catch (Exception e)

...{
MessageBox.Show(e.Message);
}
}

private void SetFootStyle()

...{
FootGrid = new DataGridView();
FootGrid.AllowUserToAddRows = false;
FootGrid.AllowUserToDeleteRows = false;
FootGrid.AllowUserToResizeRows = false;
FootGrid.AllowUserToResizeColumns = false;
FootGrid.BackgroundColor = this.BackgroundColor;
FootGrid.BorderStyle = BorderStyle.None ;
FootGrid.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
FootGrid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
FootGrid.ColumnHeadersDefaultCellStyle = this.ColumnHeadersDefaultCellStyle;
FootGrid.GridColor = this.GridColor;
FootGrid.MultiSelect = false;
FootGrid.ReadOnly = true;
FootGrid.RowHeadersVisible = false;
FootGrid.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing;
FootGrid.AllowUserToResizeRows = false;
FootGrid.ColumnHeadersHeight = this.ColumnHeadersHeight;
FootGrid.ScrollBars = ScrollBars.Horizontal;
FootGrid.ShowCellErrors = false;
FootGrid.ShowEditingIcon = false;
FootGrid.ShowRowErrors = false;


int colHeadHeight = (this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) > this.Width ?
this.ColumnHeadersHeight * 2 : this.ColumnHeadersHeight);

this.Height -= colHeadHeight;
FootGrid.Location = this.Location + new Size(0, this.Height);
FootGrid.Size = new Size(this.Width, colHeadHeight);
this.Parent.Controls.Add(FootGrid);
switch (this.Dock)

...{
case DockStyle.Top:
FootGrid.BringToFront();
FootGrid.Dock = DockStyle.Top;
break;
case DockStyle.None:
FootGrid.Dock = DockStyle.None;
break;
default:
FootGrid.Dock = DockStyle.Bottom;
break;
}
this.ScrollBars = ScrollBars.Vertical;
this._footvisible = true;
this.FootGrid.Scroll += new ScrollEventHandler(this.FootGrid_Scroll);
this.ColumnWidthChanged += new DataGridViewColumnEventHandler(this.DataGridEx_ColumnWidthChanged);
this.DataSourceChanged += new EventHandler(this.DataGridEx_DataSourceChanged);
this.Scroll -= new ScrollEventHandler(this.DataGridEx_Scroll);
}
#endregion


设置双表头#region 设置双表头

public void SetDoubleHead(string[] columnText,int[] columnOffset)

...{
if (DoubleGrid == null)

...{
SetDoubleStyle();
}
this.doubleGridOffset = columnOffset;
DoubleGrid.ColumnCount = columnOffset.Length;
for (int i = 0; i <columnOffset.Length; i++)

...{
if (columnOffset[i] < this.ColumnCount)

...{
int colwith = 0;
for (int j =(i==0?0: columnOffset[i-1] + 1); j <= columnOffset[i]; j++)

...{
if (this.Columns[j].Visible)

...{
colwith += this.Columns[j].Width;
}
}
DoubleGrid.Columns[i].Width = colwith;
DoubleGrid.Columns[i].HeaderText = columnText[i];
DoubleGrid.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable;
}
}

}

private void SetDoubleStyle()

...{
DoubleGrid = new DataGridView();

DoubleGrid.AllowUserToAddRows = false;
DoubleGrid.AllowUserToDeleteRows = false;
DoubleGrid.AllowUserToResizeRows = false;
DoubleGrid.AllowUserToResizeColumns = false;
DoubleGrid.BackgroundColor = this.BackgroundColor;
DoubleGrid.BorderStyle = BorderStyle.None;
DoubleGrid.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
DoubleGrid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
DoubleGrid.ColumnHeadersDefaultCellStyle.BackColor = this.ColumnHeadersDefaultCellStyle.BackColor ;
DoubleGrid.ColumnHeadersDefaultCellStyle.Font = this.ColumnHeadersDefaultCellStyle.Font;
DoubleGrid.ColumnHeadersDefaultCellStyle.ForeColor = this.ColumnHeadersDefaultCellStyle.ForeColor;
DoubleGrid.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
DoubleGrid.GridColor = this.GridColor;
DoubleGrid.MultiSelect = false;
DoubleGrid.ReadOnly = true;
DoubleGrid.RowHeadersVisible = false;
DoubleGrid.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing;
DoubleGrid.ColumnHeadersHeight = this.ColumnHeadersHeight;
DoubleGrid.ScrollBars = ScrollBars.Horizontal;
DoubleGrid.ShowCellErrors = false;
DoubleGrid.ShowEditingIcon = false;
DoubleGrid.ShowRowErrors = false;


DoubleGrid.Location = this.Location;
this.Height -= this.ColumnHeadersHeight;
this.Location += new Size(0, this.ColumnHeadersHeight);
DoubleGrid.Size = new Size(this.Width, this.ColumnHeadersHeight);
this.ColumnWidthChanged += new DataGridViewColumnEventHandler(this.DataGridEx_ColumnWidthChanged);
this.Parent.Controls.Add(DoubleGrid);
switch (this.Dock)

...{
case DockStyle.Bottom:
DoubleGrid.BringToFront();
DoubleGrid.Dock = DockStyle.Bottom;
break;
case DockStyle.None:
DoubleGrid.Dock = DockStyle.None;
break;
default:
DoubleGrid.Dock = DockStyle.Top;
break;
}
this._doublevisible = true;

}
#endregion

public void ReBind()

...{
if (_footvisible)

...{
try

...{
FootGrid.HorizontalScrollingOffset = 0;

decimal[] Compute = new decimal[FootText.Length];
for (int j = 0; j < this.ColumnCount; j++)

...{
if (FootText[j] != string.Empty)

...{
if (FootText[j] == "S")

...{
for (int k = 0; k < this.RowCount; k++)

...{
Compute[j] += Convert.ToDecimal(this[j, k].Value == DBNull.Value ? 0 : this[j, k].Value);
}
FootGrid.Columns[j].HeaderText = Compute[j].ToString(this.Columns[j].DefaultCellStyle.Format);
}
else

...{
FootGrid.Columns[j].HeaderText = FootText[j] + this.RowCount;
}
}
}
}
catch (DataException e)

...{
MessageBox.Show(e.Message + "请重新设置数据格式!");
}
}
}

private void FootGrid_Scroll(object sender, ScrollEventArgs e)

...{
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)

...{
this.DoubleBuffered = true;
this.HorizontalScrollingOffset = e.NewValue;
if (DoubleGrid != null)

...{
DoubleGrid.HorizontalScrollingOffset = e.NewValue;
}
this.DoubleBuffered = false;
}
}

private void DataGridEx_Scroll(object sender, ScrollEventArgs e)

...{
if (DoubleGrid != null && FootGrid == null && e.ScrollOrientation == ScrollOrientation.HorizontalScroll)

...{
DoubleGrid.HorizontalScrollingOffset = e.NewValue;
}
}

private void DataGridEx_DataSourceChanged(object sender, EventArgs e)

...{
ReBind();
}

private void DataGridEx_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)

...{
if (_footvisible )

...{
this.HorizontalScrollingOffset = FootGrid.HorizontalScrollingOffset;
}
}



private void DataGridEx_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)

...{
if (_footvisible)

...{
this.HorizontalScrollingOffset = FootGrid.HorizontalScrollingOffset;
int colHeadHeight = (this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) > this.Width ?
FootGrid.ColumnHeadersHeight * 2 : FootGrid.ColumnHeadersHeight);

this.Height = initHeight - colHeadHeight - (_doublevisible ? FootGrid.ColumnHeadersHeight : 0);
FootGrid.Location = this.Location + new Size(0, this.Height);
FootGrid.Size = new Size(this.Width, colHeadHeight);

int index = e.Column.Index;
FootGrid.Columns[index].Width = e.Column.Width;
}
if (_doublevisible)

...{
int offset=0;
for (int i = 0; i < doubleGridOffset.Length; i++)

...{
if (e.Column.Index <= doubleGridOffset[i])

...{
offset = i;
break;
}
}
DoubleGrid.Columns[offset].Width = 5;
for (int j = (offset == 0 ? 1 : doubleGridOffset[offset - 1] + 1); j <= doubleGridOffset[offset]; j++)

...{
if (this.Columns[j].Visible)

...{
DoubleGrid.Columns[offset].Width += this.Columns[j].Width;
}
}
DoubleGrid.Columns[offset].Width -= 5;
DoubleGrid.HorizontalScrollingOffset = FootGrid.HorizontalScrollingOffset;
}
}

private void DataGridEx_VisibleChanged(object sender, EventArgs e)

...{
if (FootGrid!=null )

...{
FootGrid.Visible =this .Visible;
}
}

private void DataGridEx_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)

...{
if (e.Button == MouseButtons.Right && e.RowIndex > -1)

...{
this.Rows[e.RowIndex].Selected = true;
}
}

private void DataGridEx_KeyPress(object sender, KeyPressEventArgs e)

...{
if (e.KeyChar==(char)Keys.Space && this.CurrentRow !=null )

...{
this.OnCellDoubleClick(new DataGridViewCellEventArgs(0 , this.CurrentRow.Index));
}
}

}


}
说明:DATAGRIDVIEW里提供了SCROLL事件,可以做出滚动条同步的效果。另外新增的COLUMNWIDTHCHANGED事件,可以在主表列宽改变时同步改变合计栏、双表头的列宽。