MergedDataGridView 控件
一个 DataGridView 控件,允许在合并的行中显示一些额外信息。 使用 Visual Basic.NET 2008 和 Framework 3.5。

引言
众所周知,DataGridView 控件不允许我们合并单元格,如果我们稍微思考一下,我们可以问自己,“为什么?”。 好吧,DataGridView 绑定到一个数据源(即使您没有定义它),并且每个单元格代表记录中的一个字段,那么合并的单元格将属于哪个字段? 也许,正是因为这个原因,微软没有包含这个特性。 但是,有时我们可能想要显示一些额外的信息,唯一的解决方案是弹出一个表单/消息框,或者“窃取”当前表单中的一些空间并用文本框、组合框等填充它。
此自定义的目的是在 DataGridView 中显示一些额外信息。 它基本上使用一个 RichTextBox,插入到网格中并根据父行的大小调整大小。 父行必须具有唯一的 ID,该 ID 将是文本框的名称。 然后,这将用于调整大小并将 RichTextBox 放置在正确的位置。
我还包括了一些图标动画和网格自定义,以改善最终外观。
使用代码
首先,您必须在应用程序中包含类 MergedDataGridView。 构建项目后,MergedDataGridView 控件将在工具箱中可用。 然后,只需将其拖到您的表单即可。
接下来,您需要为 MergedDataGridView 定义自定义属性
With Me.MergedDataGridView1
 
    ' Define the datasource
    .DataSource = ds.Tables(0).DefaultView
    ' Custom definitions for the RichTextBox
    .StartColumnIndex = 1
    .EndColumnIndex = 7
    .RowHeight = 60
    ' Custom definitions for the DataGridView
    .AllowUserToAddRows = False
    .AllowUserToDeleteRows = False
    .RowsDefaultCellStyle.BackColor = Color.White
    .AlternatingRowsDefaultCellStyle.BackColor = Color.AliceBlue
    .AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells)
End With 
之后,您可以包含两个 DataGridViewImageColumn,它们将用于显示 RichTextBox 和显示包含信息的 MessageBox。
' Create an image column in the datagrid that will open the merged row
Dim ImageColumn1 As New DataGridViewImageColumn
ImageColumn1.DefaultCellStyle.Alignment = DataGridViewContentAlignment.TopCenter
ImageColumn1.Image = My.Resources.DownArrow
ImageColumn1.Width = 25
' Create an image column in the datagrid that will open an extra window
Dim ImageColumn2 As New DataGridViewImageColumn
ImageColumn2.DefaultCellStyle.Alignment = DataGridViewContentAlignment.TopCenter
ImageColumn2.Image = My.Resources.Info
ImageColumn2.Width = 25
' Add the two columns to the datagridview
Me.MergedDataGridView1.Columns.AddRange(New _
   DataGridViewImageColumn() {ImageColumn1, ImageColumn2})
最后,在 CellMouseClick 事件中,检查用户是否单击了正确的列;如果是,则添加新行
' Add a new row in the correct postion (e.RowIndex + 1)
Dim rowPos As Integer = e.RowIndex + 1
Dim dv As DataView = Me.MergedDataGridView1.DataSource
Dim row As DataRow = dv.Table.NewRow()
dv.Table.Rows.InsertAt(row, rowPos)
' Get the text from the hidden columns that will be used to fill the RichTextBox
Dim mergedRowText As New System.Text.StringBuilder
mergedRowText.AppendLine(Me.MergedDataGridView1("Description", e.RowIndex).Value.ToString)
mergedRowText.AppendLine(Me.MergedDataGridView1("Link", e.RowIndex).Value.ToString)
' Call the AddMergedRow sub
Me.MergedDataGridView1.AddMergedRow(rowPos, mergedRowText.ToString)
或删除它
' Remove the row from the datasource
Dim rowPos As Integer = e.RowIndex + 1
Dim dv As DataView = Me.MergedDataGridView1.DataSource
dv.Table.Rows.RemoveAt(rowPos)
' Call the RemoveMergedRow sub
Me.MergedDataGridView1.RemoveMergedRow(Me.MergedDataGridView1(0, _
                                       e.RowIndex).Value)
正如您在随附的示例中看到的那样,表单中使用的其他代码用于验证、错误处理和通用动画。
查看控件

该控件只有两个方法:AddMergedRow 和 RemoveMergedRow,以及三个属性。 这些属性只是指示 RichTextBox 将从哪一列开始、结束以及它将具有的高度。
AddMergedRow 查找前一行的编号(ID),这将是父行,并在新行中创建一个新的 RichTextBox,并将该 ID 作为名称。
''' <summary>
''' Adds a new row with a merged cell using a richtextbox
''' </summary>
''' <param name="rowIndex">Index where the row will be added</param>
''' <param name="cellText">Text that will be displayed on the RichTextBox</param>
''' <remarks></remarks>
Public Sub AddMergedRow(ByVal rowIndex As Integer, ByVal cellText As String)
    Try
        Me.SuspendLayout()
        ' Defines the location/size of the textbox
        Dim x As Integer = _
            Me.GetColumnDisplayRectangle(Me.StartColumnIndex, False).Left + 1
        Dim y As Integer = Me.GetRowDisplayRectangle(rowIndex, False).Top
        Dim w As Integer = _
            Me.GetColumnDisplayRectangle(Me.EndColumnIndex, False).Right - x - 2
        Dim h As Integer = Me.GetRowDisplayRectangle(rowIndex, False).Size.Height - 1
        ' Gets the ID from the previous row, that will be used for the name
        ' of the textbox. This ID will be used to find the control for the row
        Dim parentRowID As Integer = Me(0, rowIndex - 1).Value
        ' Creates a new textbox and place it in the right position
        Dim rtb As New RichTextBox
        With rtb
            .Name = parentRowID
            .Text = cellText
            .Multiline = True
            .BorderStyle = BorderStyle.None
            .ScrollBars = ScrollBars.Vertical
            .ReadOnly = True
            .Font = New Font(Me.DefaultCellStyle.Font, Me.DefaultCellStyle.Font.Style)
            .SetBounds(x, y, w, h)
        End With
        Me.Controls.Add(rtb)
 
        ' Define the same color for the RichTextBox as the row color
        rtb.BackColor = Me(0, rowIndex).InheritedStyle.BackColor
        ' Define the row height
        Me.Rows(rowIndex).Height = Me.RowHeight
        ' Define a new image for the imagecell (up arrow)
        Dim arrow As DataGridViewImageCell = Me(Me.ColumnCount - 2, rowIndex - 1)
        arrow.Value = My.Resources.UpArrow
    Catch ex As Exception
        Throw New ArgumentException(ex.Message)
    Finally
        Me.ResumeLayout()
    End Try
End Sub
第二个方法 RemoveMergedRow 查找父行并将其从网格中删除。
''' <summary>
''' Removes the cell (RichTextBox) from the DataGridView
''' </summary>
''' <param name="rowID">ID of the row</param>
''' <remarks></remarks>
Public Sub RemoveMergedRow(ByVal rowID As Integer)
    Try
        ' Find the control in the DataGridView and remove it
        Dim ctrl() As Control = Me.Controls.Find(rowID.ToString, False)
        If ctrl.Length = 1 Then
            Me.Controls.Remove(ctrl(0))
        End If
        ' Define a new image for the imagecell (down arrow)
        Dim arrow As DataGridViewImageCell = Me(Me.ColumnCount - 2, Me.CurrentRow.Index)
        arrow.Value = My.Resources.DownArrow
    Catch ex As Exception
        Throw New ArgumentException(ex.Message)
    Finally
        Me.ResumeLayout()
    End Try
End Sub
Paint 事件排列 RichTextBoxes 的位置并调整大小。
由于在排序后很难计算位置并保持父行下方的空行,因此我已在 ColumnAdded 事件中关闭了排序。
关注点
此控件不仅在 DataGridView 控件上显示一些额外信息,而且还演示了如何自定义网格和处理行定位,这可能对其他项目有用。
我真的希望这能帮助改进您的项目,或者帮助您获得一些新想法。




