Jo Muncher's Blog

the way to my Dream

原创 VS2005的DataGridView 多维合并标题 功能拓展收藏

新一篇: vs2005 DataGridView 多维合并标题 2点修改 | 旧一篇: Regex 检验器

        前几日,因为项目需要做一个可共用的控件类库。其中就需要DataGridView的合并表头功能。 
在网上搜了一些资料,也下了一些自定义控件,观其效果离项目需要相去甚远。所以决定参考一下网上各位大大们的成果,自己做一个符合需要的DataGridView合并表头功能。
       很多资料都是做2维的表头,其实基本上2维的合并标题就够用了,可惜我做的项目是MES相关的,需要多维的合并标题。思考了一番,决定导入树图(TreeView)的概念解决这个问题。
       想法是这样:
       1。在多维标题中每一个最底层的标题相当于Tree中的最低深度的节点。而且这个Tree中有一个Root节点, 而且只有Root节点不予多维标题中的标题相匹配。
       2。当前标题上面的合并标题相当于Tree中与当前节点的Parent节点,标题名称与节点名称相匹配。
       3。当前标题的宽度为与之匹配的Tree节点的所有Children节点的宽度之和。
       4。从最底层节点开始绘起,当当前节点没有前驱节点时(firstnode),向上搜索继续绘制其父节点,
            依次递归,否则该节点只绘制自己完事。
       5。如果当前节点只有一个子节点则竖向合并掉子节点区域进行重绘。

       想法很简单,但为了能够给使用者一个比较友好的使用接口,另一方面也是本人水平实在有限,颇费了一些时日,想起来真是汗颜啊。

           上边的图或许可以给更直观的印象,图中Root节点是不予标题匹配的,而如果一个节点没有复数个的子节点它将显示为一个竖向合并的大标题(标题名称取回溯没有复数个字节点最上节点的文本(Root除外))。

代码如下:

Imports System
Imports
 System.ComponentModel
Imports
 System.Windows.Forms
Imports
 System.Drawing

 
Public 
Class HeaderUnitView
        
Inherits
 DataGridView

        
Private _columnTreeView() As
 TreeView
        
Private _columnList As New
 ArrayList
        
Private _cellHeight As Integer = 25

        
Private _columnDeep As Integer = 1

        
<Description("设置或获得合并表头树的深度")> _
        
Public 
Property ColumnDeep() As Integer
            
Get
                
If Me.Columns.Count = 0 Then
                    _columnDeep 
= 1
                
End If
                
Me.ColumnHeadersHeight = _cellHeight * _columnDeep
                
Return
 _columnDeep
            
End Get

            
Set(ByVal value As Integer)
                
If value < 1 Then

                    _columnDeep 
= 1
                
Else
                    _columnDeep 
= value
                
End If

                
Me.ColumnHeadersHeight = _cellHeight * _columnDeep
            
End Set

        
End Property


        
<Description("添加合并式单元格绘制的所需要的节点对象")> _
        
Public 
Property ColumnTreeView() As TreeView()
            
Get

                
Return _columnTreeView
            
End Get

            
Set(ByVal value As TreeView())
                
If Not _columnTreeView Is Nothing Then

                    
For i As Integer = 0 To _columnTreeView.Length - 1
                        _columnTreeView(i).Dispose()
                    
Next
                
End If
                _columnTreeView 
= value
            
End Set

        
End Property


        
<Description("设置添加的字段树的相关属性")> _
        
Public ReadOnly 
Property ColumnTreeViewNode() As TreeView
            
Get

                
Return _columnTreeView(0)
            
End Get

        
End Property


        
Public ReadOnly Property NadirColumnList() As ArrayList
            
Get

                
If _columnTreeView Is Nothing Then
                    
Return Nothing
                
End If
                
If _columnTreeView(0Is Nothing Then
                    
Return Nothing
                
End If
                
If _columnTreeView(0).Nodes Is Nothing Then
                    
Return Nothing
                
End If
                
If _columnTreeView(0).Nodes.Count = 0 Then
                    
Return Nothing
                
End If
                _columnList.Clear()
                GetNadirColumnNodes(_columnList _
                                , _columnTreeView(
0).Nodes(0) _
                                , 
False
)
                
Return
 _columnList
            
End Get

        
End Property



        
''' <summary>
        ''' 绘制合并表头
        ''' </summary>
        ''' <param name="node">合并表头节点</param>
        ''' <param name="e">绘图参数集</param>
        ''' <param name="level">结点深度</param>
        ''' <remarks></remarks>
        Private Sub PaintUnitHeader( _
                    
ByVal node As
 TreeNode, _
                    
ByVal e As
 System.Windows.Forms.DataGridViewCellPaintingEventArgs, _
                    
ByVal level As Integer
)
            
'根节点时退出递归调用

            If level = 0 Then
                
Return
            
End If
            
'处理
            Me.Update()

            
Dim uhRectangle As
 Rectangle
            
Dim uhWidth As Integer

            
Dim gridBrush As New SolidBrush(Me.GridColor)
            
Dim backColorBrush As New
 SolidBrush(e.CellStyle.BackColor)
            
Dim gridLinePen As New
 Pen(gridBrush)
            
Dim textFormat As New
 StringFormat()
            textFormat.Alignment 
=
 StringAlignment.Center

            uhWidth 
=
 GetUnitHeaderWidth(node)

            
If IsSingleChildNode(node) Then

                uhRectangle 
= New Rectangle( _
                                    e.CellBounds.Left, _
                                    e.CellBounds.Top 
+ (level - 1*
 _cellHeight, _
                                    uhWidth 
- 1
, _
                                    _cellHeight 
* (_columnDeep - level + 1- 1
)
            
Else


                uhRectangle 
= New Rectangle( _
                                    e.CellBounds.Left, _
                                    e.CellBounds.Top 
+ (level - 1*
 _cellHeight, _
                                    uhWidth 
- 1
, _
                                    _cellHeight 
- 1
)
            
End If

            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)
            
'写字段文本


            
'e.Graphics.DrawString(node.Text _
            '                        , Me.Font _
            '                        , Brushes.Black _
            '                        , uhRectangle _
            '                        , textFormat)
            e.Graphics.DrawString(node.Text _
                                    , 
Me
.Font _
                                    , Brushes.Black _
                                    , uhRectangle.Left 
+
 _
                                    uhRectangle.Width 
/ 2 -
 _
                                    e.Graphics.MeasureString(node.Text, 
Me.Font).Width / 2 - 1
 _
                                    , uhRectangle.Top 
+
 _
                                    uhRectangle.Height 
/ 2 -
 _
                                    e.Graphics.MeasureString(node.Text, 
Me.Font).Height / 2
)


            
'递归调用()


            
If node.PrevNode Is Nothing Then
                
If Not node.Parent Is Nothing Then
                    PaintUnitHeader(node.Parent, e, level 
- 1)
                
End If

            
End If

        
End Sub


        
Private Function IsSingleChildNode( _
                        
ByVal node As
 TreeNode) _
                        
As Boolean

            
If node.Nodes Is Nothing Then
                
Return False
            
End If
            
If node.Nodes.Count = 0 Then
                
Return False
            
End If
            
If node.Nodes.Count = 1 Then
                
Return True
            
End If
            
Return False
        
End Function



        
''' <summary>
        ''' 获得合并标题字段的宽度
        ''' </summary>
        ''' <param name="node">字段节点</param>
        ''' <returns>字段宽度</returns>
        ''' <remarks></remarks>
        Private Function GetUnitHeaderWidth(ByVal node As TreeNode) As Integer
            
'获得非最底层字段的宽度
            Dim uhWidth As Integer = 0
            
'获得最底层字段的宽度
            If node.Nodes Is Nothing Then
                
Return Me.Columns(GetColumnListNodeIndex(node)).Width
            
End If

            
If node.Nodes.Count = 0 Then
                
Return Me.Columns(GetColumnListNodeIndex(node)).Width
            
End If

            
For i As Integer = 0 To node.Nodes.Count - 1
                uhWidth 
= uhWidth + GetUnitHeaderWidth(node.Nodes(i))
            
Next

            
Return uhWidth
        
End Function


        
''' <summary>
        ''' 获得底层字段索引
        ''' </summary>
        ''' <param name="node">底层字段节点</param>
        ''' <returns>索引</returns>
        ''' <remarks></remarks>
        Private Function GetColumnListNodeIndex(ByVal node As TreeNode) As Integer
            
For i As Integer = 0 To _columnList.Count - 1
                
If CType(_columnList(i), TreeNode).Equals(node) Then
                    
Return i
                
End If

            
Next
            
Return -1
        
End Function


        
''' <summary>
        ''' 获得底层字段集合
        ''' </summary>
        ''' <param name="alList">底层字段集合</param>
        ''' <param name="node">字段节点</param>
        ''' <param name="checked">向上搜索与否</param>
        ''' <remarks></remarks>
        Private Sub GetNadirColumnNodes( _
                    
ByVal alList As
 ArrayList, _
                    
ByVal node As
 TreeNode, _
                    
ByVal checked As Boolean
)
            
If checked = False Then

                
If node.FirstNode Is Nothing Then
                    alList.Add(node)
                    
If Not node.NextNode Is Nothing Then
                        GetNadirColumnNodes(alList, node.NextNode, 
False)
                        
Return

                    
End If
                    
If Not node.Parent Is Nothing Then
                        GetNadirColumnNodes(alList, node.Parent, 
True)
                        
Return

                    
End If
                
Else
                    
If Not node.FirstNode Is Nothing Then
                        GetNadirColumnNodes(alList, node.FirstNode, 
False)
                        
Return

                    
End If
                
End If
            
Else
                
If node.FirstNode Is Nothing Then

                
Else
                    
If Not node.NextNode Is Nothing Then
                        GetNadirColumnNodes(alList, node.NextNode, 
False)
                        
Return

                    
End If
                    
If Not node.Parent Is Nothing Then
                        GetNadirColumnNodes(alList, node.Parent, 
True)
                        
Return

                    
End If
                
End If
            
End If

        
End Sub


        
''' <summary>
        ''' 单元格绘制(重写)
        ''' </summary>
        ''' <param name="e"></param>