DataGridView多層表頭的制作(3)
引言:
受网上文章的啟發,終于用樹做出了多層表頭.將代碼做了一些修改,比較滿意,現在就只有一點不滿意,那就是ColumnHeadersHeightSizeMode仍要設為DisableResizing.先看效果.
原理:
利用TreeView的Nodes屬性保存表頭信息,也還是重載OnCellPainting,支持設計時的可視.支持表頭顏色自定義.
開發環境:vs2005
源碼:
TreeHeadDataGridView.cs
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
namespace CellPaintingDataGridView
{
public partial class TreeHeadDataGridView : DataGridView
{
private TreeView treeView1=new TreeView();
public TreeHeadDataGridView()
{
InitializeComponent();
}
public TreeHeadDataGridView(IContainer container)
{
container.Add(this);
InitializeComponent();
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public TreeNodeCollection HeadSource
{
get { return this.treeView1.Nodes; }
}
private int _cellHeight = 17;
private int _columnDeep = 1;
[Description("设置或获得合并表头树的深度")]
public int ColumnDeep
{
get
{
if (this.Columns.Count == 0)
_columnDeep = 1;
this.ColumnHeadersHeight = _cellHeight * _columnDeep;
return _columnDeep;
}
set
{
if (value < 1)
_columnDeep = 1;
else
_columnDeep = value;
this.ColumnHeadersHeight = _cellHeight * _columnDeep;
}
}
///<summary>
///绘制合并表头
///</summary>
///<param name="node">合并表头节点</param>
///<param name="e">绘图参数集</param>
///<param name="level">结点深度</param>
///<remarks></remarks>
public void PaintUnitHeader(TreeNode node, System.Windows.Forms.DataGridViewCellPaintingEventArgs e, int level)
{
//根节点时退出递归调用
if (level == 0)
return;
RectangleF uhRectangle;
int uhWidth;
SolidBrush gridBrush = new SolidBrush(this.GridColor);
Pen gridLinePen = new Pen(gridBrush);
StringFormat textFormat = new StringFormat();
textFormat.Alignment = StringAlignment.Center;
uhWidth = GetUnitHeaderWidth(node);
//与原贴算法有所区别在这。
if (node.Nodes.Count == 0)
{
uhRectangle = new Rectangle(e.CellBounds.Left,
e.CellBounds.Top + node.Level * _cellHeight,
uhWidth - 1,
_cellHeight * (_columnDeep - node.Level) - 1);
}
else
{
uhRectangle = new Rectangle(
e.CellBounds.Left,
e.CellBounds.Top + node.Level * _cellHeight,
uhWidth - 1,
_cellHeight - 1);
}
Color backColor = e.CellStyle.BackColor;
if (node.BackColor != Color.Empty)
{
backColor = node.BackColor;
}
SolidBrush backColorBrush = new SolidBrush(backColor);
//画矩形
e.Graphics.FillRectangle(backColorBrush, uhRectangle);
//划底线
e.Graphics.DrawLine(gridLinePen
, uhRectangle.Left
, uhRectangle.Bottom
, uhRectangle.Right
, uhRectangle.Bottom);
//划右端线
e.Graphics.DrawLine(gridLinePen
, uhRectangle.Right
, uhRectangle.Top
, uhRectangle.Right
, uhRectangle.Bottom);
写字段文本
Color foreColor = Color.Black;
if (node.ForeColor != Color.Empty)
{
foreColor = node.ForeColor;
}
e.Graphics.DrawString(node.Text, this.Font
, new SolidBrush(foreColor)
, uhRectangle.Left + uhRectangle.Width / 2 -
e.Graphics.MeasureString(node.Text, this.Font).Width / 2 - 1
, uhRectangle.Top +
uhRectangle.Height / 2 - e.Graphics.MeasureString(node.Text, this.Font).Height / 2);
递归调用()
if (node.PrevNode == null)
if (node.Parent != null)
PaintUnitHeader(node.Parent, e, level - 1);
}
/// <summary>
/// 获得合并标题字段的宽度
/// </summary>
/// <param name="node">字段节点</param>
/// <returns>字段宽度</returns>
/// <remarks></remarks>
private int GetUnitHeaderWidth(TreeNode node)
{
//获得非最底层字段的宽度
int uhWidth = 0;
//获得最底层字段的宽度
if (node.Nodes == null)
return this.Columns[GetColumnListNodeIndex(node)].Width;
if (node.Nodes.Count == 0)
return this.Columns[GetColumnListNodeIndex(node)].Width;
for (int i = 0; i <= node.Nodes.Count - 1; i++)
{
uhWidth = uhWidth + GetUnitHeaderWidth(node.Nodes[i]);
}
return uhWidth;
}
/// <summary>
/// 获得底层字段索引
/// </summary>
/// <param name="node">底层字段节点</param>
/// <returns>索引</returns>
/// <remarks></remarks>
private int GetColumnListNodeIndex(TreeNode node)
{
for (int i = 0; i <= _columnList.Count - 1; i++)
{
if (((TreeNode)_columnList[i]).Equals(node))
return i;
}
return -1;
}
private List<TreeNode> _columnList = new List<TreeNode>();
[Description("最底層結點集合")]
public List<TreeNode> NadirColumnList
{
get
{
if (this.treeView1 == null)
return null;
if (this.treeView1.Nodes == null)
return null;
if (this.treeView1.Nodes.Count == 0)
return null;
_columnList.Clear();
foreach (TreeNode node in this.treeView1.Nodes)
{
//GetNadirColumnNodes(_columnList, node, false);
GetNadirColumnNodes(_columnList, node);
}
return _columnList;
}
}
private void GetNadirColumnNodes(List<TreeNode> alList, TreeNode node)
{
if (node.Parent == null)
{
if (node.FirstNode == null)
{
alList.Add(node);
}
else
{
GetNadirColumnNodes(alList, node.FirstNode);
}
}
else
{
if (node.FirstNode == null)
{
alList.Add(node);
if (node.NextNode != null)
{
GetNadirColumnNodes(alList, node.NextNode);
}
}
else
{
GetNadirColumnNodes(alList, node.FirstNode);
}
}
}
/// <summary>
/// 获得底层字段集合
/// </summary>
/// <param name="alList">底层字段集合</param>
/// <param name="node">字段节点</param>
/// <param name="checked">向上搜索与否</param>
/// <remarks></remarks>
private void GetNadirColumnNodes(List<TreeNode> alList, TreeNode node, Boolean isChecked)
{
if (isChecked == false)
{
if (node.FirstNode == null)
{
alList.Add(node);
if (node.NextNode != null)
{
GetNadirColumnNodes(alList, node.NextNode, false);
return;
}
if (node.Parent != null)
{
GetNadirColumnNodes(alList, node.Parent, true);
return;
}
}
else
{
if (node.FirstNode != null)
{
GetNadirColumnNodes(alList, node.FirstNode, false);
return;
}
}
}
else
{
if (node.FirstNode == null)
{
return;
}
else
{
if (node.NextNode != null)
{
GetNadirColumnNodes(alList, node.NextNode, false);
return;
}
if (node.Parent != null)
{
GetNadirColumnNodes(alList, node.Parent, true);
return;
}
}
}
}
/// <summary>
/// 单元格绘制(重写)
/// </summary>
/// <param name="e"></param>
/// <remarks></remarks>
protected override void OnCellPainting(System.Windows.Forms.DataGridViewCellPaintingEventArgs e)
{
//行标题不重写
if (e.ColumnIndex < 0)
{
base.OnCellPainting(e);
return;
}
if (_columnDeep == 1)
{
base.OnCellPainting(e);
return;
}
//绘制表头
if (e.RowIndex == -1)
{
PaintUnitHeader((TreeNode)NadirColumnList[e.ColumnIndex], e, _columnDeep);
e.Handled = true;
}
}
}
}
TreeHeadDataGridView.Design.cs(主要是修改一下Dispose函數)
protected override void Dispose(bool disposing)
{
if (this.treeView1 != null)
{
this.treeView1.Dispose();
}
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
調用:
直接拖動自定義控件到界面上設置屬性即可.
我的另2篇多表頭文章:
http://blog.csdn.net/manimanihome/archive/2008/01/22/2058514.aspx
http://blog.csdn.net/manimanihome/archive/2008/03/31/2233573.aspx
參考文獻:
(VB實現) http://blog.csdn.net/JoMuncher/archive/2007/11/02/1862977.aspx
(C#實現) http://blog.csdn.net/teacatcn/archive/2008/01/23/2060482.aspx