VB.net DataGridView双表头
用3个AI都没搞定.
最后用来园子中博友的代码修改才成功 分享给大家.
Public Class ColumPairDic
Inherits Dictionary(Of Integer, Dictionary(Of Integer, Integer))
End Class
Friend Class RowPairDic
Inherits Dictionary(Of Integer, Integer)
End Class
Public Class HeaderInfo
Public RowCount As Integer
Public ColCount As Integer
Public CellNames As String(,)
Public CellBorders As HeaderCellBorderInfo(,)
Public Alignments As DataGridViewContentAlignment(,)
Public CellCrosses As Rectangle(,)
Public HeaderCellInfoList As List(Of HeaderCellInfo)
Public Class HeaderCellBorderInfo
Public Top As Boolean = False
Public Left As Boolean = False
Public Right As Boolean = False
Public Bottom As Boolean = False
End Class
Public Sub New(rowCount As Integer, colCount As Integer, headerCellInfoList As List(Of HeaderCellInfo))
Me.RowCount = rowCount
Me.ColCount = colCount
Me.HeaderCellInfoList = headerCellInfoList
InitHeaderInfo()
End Sub
Private Sub InitHeaderInfo()
CellNames = New String(RowCount - 1, ColCount - 1) {}
CellBorders = New HeaderCellBorderInfo(RowCount - 1, ColCount - 1) {}
Alignments = New DataGridViewContentAlignment(RowCount - 1, ColCount - 1) {}
CellCrosses = New Rectangle(RowCount - 1, ColCount - 1) {}
For i As Integer = 0 To RowCount - 1
For j As Integer = 0 To ColCount - 1
CellNames(i, j) = String.Empty
CellBorders(i, j) = New HeaderCellBorderInfo()
Alignments(i, j) = DataGridViewContentAlignment.MiddleCenter
Next
Next
For i As Integer = 0 To RowCount - 1
CellBorders(i, 0).Left = True
CellBorders(i, ColCount - 1).Right = True
Next
For j As Integer = 0 To ColCount - 1
CellBorders(0, j).Top = True
CellBorders(RowCount - 1, j).Bottom = True
Next
For Each headerCellInfo As HeaderCellInfo In HeaderCellInfoList
For i As Integer = headerCellInfo.Row To headerCellInfo.Row + headerCellInfo.RowSpan - 1
For j As Integer = headerCellInfo.Col To headerCellInfo.Col + headerCellInfo.ColSpan - 1
CellNames(i, j) = headerCellInfo.CellName
Alignments(i, j) = headerCellInfo.Alignment
CellCrosses(i, j) = New Rectangle(headerCellInfo.Col, headerCellInfo.Row, headerCellInfo.ColSpan, headerCellInfo.RowSpan)
Next
Next
For i As Integer = headerCellInfo.Row To headerCellInfo.Row + headerCellInfo.RowSpan - 1
CellBorders(i, headerCellInfo.Col + headerCellInfo.ColSpan - 1).Right = True
Next
For j As Integer = headerCellInfo.Col To headerCellInfo.Col + headerCellInfo.ColSpan - 1
CellBorders(headerCellInfo.Row + headerCellInfo.RowSpan - 1, j).Bottom = True
Next
Next
End Sub
End Class
Public Class HeaderCellInfo
Public Row As Integer
Public Col As Integer
Public RowSpan As Integer = 1
Public ColSpan As Integer = 1
Public CellName As String = String.Empty
Public Alignment As DataGridViewContentAlignment = DataGridViewContentAlignment.MiddleCenter
Public Sub New()
End Sub
Public Sub New(row As Integer, col As Integer, cellName As String)
Me.Row = row
Me.Col = col
Me.CellName = cellName
End Sub
Public Sub New(row As Integer, col As Integer, rowSpan As Integer, colSpan As Integer, cellName As String)
Me.Row = row
Me.Col = col
Me.RowSpan = rowSpan
Me.ColSpan = colSpan
Me.CellName = cellName
End Sub
Public Sub New(row As Integer, col As Integer, cellName As String, alignment As DataGridViewContentAlignment)
Me.Row = row
Me.Col = col
Me.CellName = cellName
Me.Alignment = alignment
End Sub
Public Sub New(row As Integer, col As Integer, rowSpan As Integer, colSpan As Integer, cellName As String, alignment As DataGridViewContentAlignment)
Me.Row = row
Me.Col = col
Me.RowSpan = rowSpan
Me.ColSpan = colSpan
Me.CellName = cellName
Me.Alignment = alignment
End Sub
End Class
Public Class HeaderSetting
Private Sub New()
End Sub
Private _rowCount As Integer
Public Property RowCount() As Integer
Get
Return _rowCount
End Get
Set(value As Integer)
_rowCount = value
End Set
End Property
Private _colCount As Integer
Public Property ColCount() As Integer
Get
Return _colCount
End Get
Set(value As Integer)
_colCount = value
End Set
End Property
Private _headerCellInfoList As New List(Of HeaderCellInfo)
Public Property HeaderCellInfoList() As List(Of HeaderCellInfo)
Get
Return _headerCellInfoList
End Get
Set(value As List(Of HeaderCellInfo))
_headerCellInfoList = value
End Set
End Property
End Class
Public Class DataGridViewCellMerge
Inherits DataGridView
Public Sub New()
'InitializeComponent()
Me.RowHeadersVisible = False
'Me.ColumnHeadersVisible = False
Me.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
Me.AllowUserToAddRows = False
Me.MultiSelect = False
Me.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect
End Sub
Private columDic As New ColumPairDic()
Private _headerInfo As HeaderInfo
Private _headerInfoXmlStr As String
<Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", GetType(UITypeEditor))>
Public Property HeaderInfoXmlStr() As String
Get
Return _headerInfoXmlStr
End Get
Set(value As String)
_headerInfoXmlStr = value
Try
Using stringReader As New StringReader(_headerInfoXmlStr)
Dim xmlSerializer As New XmlSerializer(GetType(HeaderSetting))
Dim headerSetting As HeaderSetting = CType(xmlSerializer.Deserialize(stringReader), HeaderSetting)
If headerSetting IsNot Nothing Then
HeaderInfo = New HeaderInfo(headerSetting.RowCount,
headerSetting.ColCount,
headerSetting.HeaderCellInfoList)
Me.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
Me.ColumnHeadersHeight = _headerRowHeight * _headerInfo.RowCount
End If
End Using
Catch
' Ignore deserialization errors
End Try
End Set
End Property
Private _headerRowHeight As Integer = 25
Public Property HeaderRowHeight As Integer
Get
Return _headerRowHeight
End Get
Set(value As Integer)
If value < 1 Then Throw New ArgumentOutOfRangeException(NameOf(HeaderRowHeight), "高度必须大于0")
_headerRowHeight = value
If _headerInfo IsNot Nothing Then
Me.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
Me.ColumnHeadersHeight = _headerRowHeight * _headerInfo.RowCount
Me.Invalidate()
End If
End Set
End Property
Public WriteOnly Property MergedRule() As ColumPairDic
Set(value As ColumPairDic)
columDic = value
End Set
End Property
Public WriteOnly Property HeaderInfo() As HeaderInfo
Set(value As HeaderInfo)
_headerInfo = value
If _headerInfo IsNot Nothing Then
Me.ColumnHeadersHeight = _headerRowHeight * _headerInfo.RowCount
End If
End Set
End Property
Protected Overrides Sub OnLayout(e As LayoutEventArgs)
MyBase.OnLayout(e)
If _headerInfo IsNot Nothing Then
Me.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
Me.ColumnHeadersHeight = _headerRowHeight * _headerInfo.RowCount
End If
End Sub
Protected Overrides Sub OnCellPainting(e As DataGridViewCellPaintingEventArgs)
Try
If e.RowIndex = -1 Then
PaintHeader(e)
Else
MyBase.OnCellPainting(e)
End If
Catch ex As Exception
' Log error if needed, but don't suppress all
System.Diagnostics.Debug.WriteLine("Painting error: " & ex.Message)
End Try
End Sub
Private Function GetMergedRows(rowDic As RowPairDic) As List(Of Integer)
Dim rowList As New List(Of Integer)()
For Each key As Integer In rowDic.Keys
Dim startRow As Integer = key
Dim endRow As Integer = rowDic(key)
For row As Integer = startRow To endRow
rowList.Add(row)
Next
Next
Return rowList
End Function
Private Sub PaintCell(e As DataGridViewCellPaintingEventArgs)
e.CellStyle.SelectionForeColor = e.CellStyle.ForeColor
e.CellStyle.SelectionBackColor = e.CellStyle.BackColor
Dim normalCell As Boolean = True
If columDic.ContainsKey(e.ColumnIndex) Then
Dim rowDic As RowPairDic = Nothing
columDic.TryGetValue(e.ColumnIndex, rowDic)
If rowDic IsNot Nothing AndAlso rowDic.Count <> 0 Then
Dim rowList As List(Of Integer) = GetMergedRows(rowDic)
If rowList.Contains(e.RowIndex) Then
normalCell = False
e.PaintBackground(e.ClipBounds, False)
' the cell is the first row of the merged cells
If rowDic.ContainsKey(e.RowIndex) Then
e.PaintContent(e.ClipBounds)
End If
' the cell is a merged cell
If rowDic.ContainsValue(e.RowIndex) Then
' draw the line under the cell
e.Graphics.DrawLine(New Pen(Me.GridColor), e.CellBounds.Left, e.CellBounds.Bottom - 1,
e.CellBounds.Right - 1, e.CellBounds.Bottom - 1)
Else
' clear the line under the cell
e.Graphics.DrawLine(New Pen(e.CellStyle.BackColor), e.CellBounds.Left, e.CellBounds.Bottom - 1,
e.CellBounds.Right - 2, e.CellBounds.Bottom - 1)
End If
End If
End If
End If
If normalCell Then
e.PaintBackground(e.ClipBounds, False)
e.PaintContent(e.ClipBounds)
End If
e.Handled = True
End Sub
Private Sub PaintHeader(e As DataGridViewCellPaintingEventArgs)
If _headerInfo Is Nothing Then Return
Dim rowTops(_headerInfo.RowCount - 1) As Integer
Dim rowBottoms(_headerInfo.RowCount - 1) As Integer
Dim totalHeight As Integer = Me.ColumnHeadersHeight
Dim rowHeight As Integer = totalHeight \ _headerInfo.RowCount
For i As Integer = 0 To _headerInfo.RowCount - 1
rowTops(i) = e.CellBounds.Top + i * rowHeight
rowBottoms(i) = rowTops(i) + rowHeight
Next
rowBottoms(_headerInfo.RowCount - 1) = e.CellBounds.Bottom
For i As Integer = 0 To _headerInfo.RowCount - 1
PaintHeaderCellBackground(e, _headerInfo.CellBorders(i, e.ColumnIndex),
e.CellBounds.Left, rowTops(i),
e.CellBounds.Right, rowBottoms(i))
Next
For i As Integer = 0 To _headerInfo.RowCount - 1
Dim cross As Rectangle = _headerInfo.CellCrosses(i, e.ColumnIndex)
Dim x2 As Integer = e.CellBounds.Left
If x2 < 2 Then x2 += 1
Dim w2 As Integer = 0
For col As Integer = cross.X To cross.X + cross.Width - 1
w2 += Me.Columns(col).Width
If col < e.ColumnIndex Then
x2 -= Me.Columns(col).Width
End If
Next
Dim y As Integer = rowTops(cross.Y)
Dim height As Integer = rowBottoms(cross.Y + cross.Height - 1) - y
PaintHeaderCellContent(e, _headerInfo.CellNames(i, e.ColumnIndex),
x2, y, w2, height,
_headerInfo.Alignments(i, e.ColumnIndex))
Next
e.Handled = True
End Sub
Private Sub PaintHeaderCellBackground(e As DataGridViewCellPaintingEventArgs,
border As HeaderInfo.HeaderCellBorderInfo,
left As Integer, top As Integer,
right As Integer, bottom As Integer)
Dim brushBack As New SolidBrush(Me.ColumnHeadersDefaultCellStyle.BackColor)
e.Graphics.FillRectangle(brushBack, left, top, right - left, bottom - top)
right -= 1
bottom -= 1
Dim penGrid As New Pen(Me.GridColor)
If border.Top Then
e.Graphics.DrawLine(penGrid, left, top, right, top)
End If
If border.Left Then
e.Graphics.DrawLine(penGrid, left, top, left, bottom)
End If
If border.Right Then
e.Graphics.DrawLine(penGrid, right, top, right, bottom)
End If
If border.Bottom Then
e.Graphics.DrawLine(penGrid, left, bottom, right, bottom)
End If
End Sub
Private Sub PaintHeaderCellContent(e As DataGridViewCellPaintingEventArgs,
colName As String,
x As Integer, y As Integer,
width As Integer, height As Integer,
alignment As DataGridViewContentAlignment)
Dim drawFormat As New StringFormat()
drawFormat.Alignment = StringAlignment.Center
drawFormat.LineAlignment = StringAlignment.Center
If alignment = DataGridViewContentAlignment.MiddleLeft Then
drawFormat.Alignment = StringAlignment.Near
End If
e.Graphics.DrawString(colName,
Me.Font,
New SolidBrush(e.CellStyle.ForeColor),
New RectangleF(x, y, width, height),
drawFormat)
End Sub
''' <summary>
''' Unite Cells联合单元格
''' </summary>
''' <param name="startRow"> start row index </param>
''' <param name="endRow"> end row index </param>
''' <param name="columnIndex"> columnIndex </param>
Public Sub UniteCells(startRow As Integer, endRow As Integer, columnIndex As Integer)
If startRow > endRow Then Return
Dim rowPairDic As New RowPairDic()
rowPairDic.Add(startRow, endRow)
columDic(columnIndex) = rowPairDic
End Sub
End Class
浙公网安备 33010602011771号