VS2005的DataGridView 多维合并标题 功能拓展

转帖|其它|编辑:郝浩|2008-09-19 14:25:15.000|阅读 4510 次

概述:VS2005的DataGridView 多维合并标题 功能拓展

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>

  前几日,因为项目需要做一个可共用的控件类库。其中就需要DataGridView的合并表头功能。
在网上搜了一些资料,也下了一些自定义控件,观其效果离项目需要相去甚远。所以决定参考一下网上各位大大们的成果,自己做一个符合需要的DataGridView合并表头功能。

  很多资料都是做2维的表头,其实基本上2维的合并标题就够用了,可惜我做的项目是MES相关的,需要多维的合并标题。思考了一番,决定导入树图(TreeView)的概念解决这个问题。

  想法是这样:

  • 在多维标题中每一个最底层的标题相当于Tree中的最低深度的节点。而且这个Tree中有一个Root节点, 而且只有Root节点不予多维标题中的标题相匹配。
  • 当前标题上面的合并标题相当于Tree中与当前节点的Parent节点,标题名称与节点名称相匹配。
  • 当前标题的宽度为与之匹配的Tree节点的所有Children节点的宽度之和。
  • 从最底层节点开始绘起,当当前节点没有前驱节点时(firstnode),向上搜索继续绘制其父节点,依次递归,否则该节点只绘制自己完事。
  • 如果当前节点只有一个子节点则竖向合并掉子节点区域进行重绘。

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

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

代码如下:

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

 Public Class HeaderUnitViewClass 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()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[SPAN]

        <Description("添加合并式单元格绘制的所需要的节点对象")> _
        Public Property ColumnTreeView()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()Property ColumnTreeViewNode() As TreeView
            Get
                Return _columnTreeView(0)
            End Get
        End Property

        Public ReadOnly Property NadirColumnList()Property NadirColumnList() As ArrayList
            Get
                If _columnTreeView Is Nothing Then
                    Return Nothing
                End If
                If _columnTreeView(0) Is 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()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)[SPAN]
            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()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()Function GetUnitHeaderWidth(ByVal node As TreeNode) As Integer[SPAN]
            '获得非最底层字段的宽度
            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()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()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[SPAN]
                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>
        ''' <remarks></remarks>
        Protected Overrides Sub OnCellPainting()Sub OnCellPainting( _
                    ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs)

            '行标题不重写
               If e.ColumnIndex < 0 Then
                   MyBase.OnCellPainting(e)
                   Return
               End If
            If _columnDeep = 1 Then
                MyBase.OnCellPainting(e)
                Return
            End If
            '绘制表头
            If e.RowIndex = -1 Then
                PaintUnitHeader(CType(NadirColumnList(e.ColumnIndex), TreeNode) _
                                , e _
                                , _columnDeep)
                e.Handled = True
            End If
        End Sub

    End Class

  使用方法为:

  • 添加所有最底层字段。在HeaderUnitView的Columns属性中添加即可,宽度自定。
  • ColumnHeadersHeightSizeMode属性不能设置为AutoSize,推荐DisableResizing。
  • 在ColumnTreeView属性中添加一个TreeView,只需添加一个,在多添加也没有用。
  • 做完第三步,下面的ColumnTreeViewNode会出现一个TreeView对象,展开ColumnTreeViewNode找到Nodes属性添加节点就可以了,注意的是要先添加一个根节点,再往下添加他的子节点。节点文本就是标题中显示的标题。
  • 还有一个重要属性是ColumnDeep,输入用户要做的多维标题的维数,如图中所示就是4了。
  • 如果做完之后觉得节点不够或者多了,可以在已经建立的TreeView上根据上述步骤1~5进行修改就可以了。

  使用注意:

  • 使用者必须对标题结构设计先有着比较正确的认识,可以正确的作出它的Tree结构。
  • 所有没有终端的节点(就是没有子节点的节点)必须放置在深度最底层。

  自我使用评价:

  • 因为是使用了Paint + 递归,所以效能比较低下,而且标题和维数不断增大的情况下是否出现OutOfStack,当然在无限增大的时候肯定是会出现的。
  • GridView的标题样式比较土,如果需要更好看的界面,就得大家自己捉摸,拓展了。
  • GridView图面刷新时,头一次刷出的效果有可能不正确,比如有文字的重叠等等。在点一下刷一次就恢复正常。
  • 编写代码的能力有限,写得比较粗糙,而且也不太规范,自己也很不满意。而且有些代码似乎可以写得更简练一些,就是懒了不愿意写了,以后再改一改,拓展一下。
  • 虽然说用TreeView方案解决了项目需要,但感觉这个不是解决问题的最佳方案,自觉应该作出一个多维标题行类这样的代码,可以进行SelfPaint,我们只需要把这些行对象添加进Gridview中就可以了,上面的问题也应该可以解决。

标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com

文章转载自:CSDN

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP