ssCAROのブログ

色んなとこで見つけたプログラムのメモ置き場っぽい

DataGridViewのセル結合(行のセルを固定)

以前に書いた記事で「DataGridViewのセル結合」をしたときだと、行を固定した場合、上手く描画できません。

その対応したプログラムを書いたのでメモ。

  • 表を見せるのみです。
  • 行列の追加、編集、削除や、セル幅の変更は考慮していません。
  • 行列のヘッダーは無効にしています。
  • DataGridViewコントロールの名前は、「DataGridView1」です。
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    Init()

End Sub

Private Sub Init()

    'ちらつき防止
    Dim type As System.Type = GetType(DataGridView)
    Dim propertyInfo As System.Reflection.PropertyInfo = type.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic)
    propertyInfo.SetValue(DataGridView1, True, Nothing)

    DataGridView1.AllowUserToAddRows = False
    DataGridView1.ReadOnly = True
    DataGridView1.AllowUserToDeleteRows = False
    DataGridView1.ColumnHeadersVisible = False
    DataGridView1.RowHeadersVisible = False

    Dim i As Integer
    Dim j As Integer
    Dim colAdd As DataGridViewColumn
    For i = 1 To 3
        colAdd = New DataGridViewColumn
        colAdd.Name = String.Format("Column{0}", i)
        colAdd.Width = 60
        colAdd.CellTemplate = New DataGridViewTextBoxCell
        DataGridView1.Columns.Add(colAdd)
    Next

    '結合したいセルには同じテキストを入れておく
    Dim cellText(,) As String = {
        {"店舗", "店舗", "売上"},
        {"店舗名", "担当エリア", "売上"},
        {"北支店01", "駅北", "10000"},
        {"北支店02", "駅南", "9000"},
        {"西支店03", "商店街", "12000"},
        {"東支店04", "住宅区", "8000"},
        {"南支店05", "商業区", "11000"},
        {"北支店06", "駅北", "10000"},
        {"北支店07", "駅南", "9000"},
        {"西支店08", "商店街", "12000"},
        {"東支店09", "住宅区", "8000"},
        {"南支店10", "商業区", "11000"},
        {"北支店11", "駅北", "10000"},
        {"北支店12", "駅南", "9000"},
        {"西支店13", "商店街", "12000"},
        {"東支店14", "住宅区", "8000"},
        {"南支店15", "商業区", "11000"},
        {"北支店16", "駅北", "10000"},
        {"北支店17", "駅南", "9000"},
        {"西支店18", "商店街", "12000"},
        {"東支店19", "住宅区", "8000"},
        {"南支店20", "商業区", "11000"}
    }
    Dim rowAdd As DataGridViewRow
    For i = 0 To cellText.GetLength(0) - 1
        rowAdd = New DataGridViewRow
        rowAdd.CreateCells(DataGridView1)
        For j = 0 To rowAdd.Cells.Count - 1
            rowAdd.Cells(j).Value = cellText(i, j)
        Next
        DataGridView1.Rows.Add(rowAdd)
    Next

    'ヘッダー行を固定とする
    DataGridView1.Rows(1).Frozen = True

End Sub

Private Sub DataGridView1_CellPainting(sender As Object, e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting

    '結合したいセルをここに書く
    MergeCell(e, New Point(0, 0), New Point(1, 0))
    MergeCell(e, New Point(2, 0), New Point(2, 1))
    MergeCell(e, New Point(0, 1), New Point(0, 1))
    MergeCell(e, New Point(1, 1), New Point(1, 1))

End Sub

'Cell1には、セルの開始位置(X, Y)
'Cell2には、セルの終了位置(X, Y)
Private Sub MergeCell(ByRef e As System.Windows.Forms.DataGridViewCellPaintingEventArgs, Cell1 As Point, Cell2 As Point)

    If (e.RowIndex >= Cell1.Y AndAlso e.RowIndex <= Cell2.Y) AndAlso (e.ColumnIndex >= Cell1.X AndAlso e.ColumnIndex <= Cell2.X) Then

        Dim rect As New Rectangle With {.X = 0, .Y = 0, .Width = 0, .Height = 0}
        Dim i As Integer
        Dim firstRowIndex As Integer

        '固定セルであれば、先頭から描画されているとする
        If DataGridView1.Rows(e.RowIndex).Frozen = True Then
            firstRowIndex = 0
        Else
            firstRowIndex = DataGridView1.FirstDisplayedScrollingRowIndex
        End If

        '開始セルの位置
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.Y + 1 To firstRowIndex
            rect.Y -= DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        For i = firstRowIndex + 1 To Cell1.Y
            rect.Y += DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.X + 1 To DataGridView1.FirstDisplayedScrollingColumnIndex
            rect.X -= DataGridView1(i - 1, Cell1.Y).Size.Width
        Next
        For i = DataGridView1.FirstDisplayedScrollingColumnIndex + 1 To Cell1.X
            rect.X += DataGridView1(i - 1, Cell1.Y).Size.Width
        Next

        '終了セルの幅
        For i = Cell1.Y To Cell2.Y
            rect.Height += DataGridView1(Cell2.X, i).Size.Height
        Next
        For i = Cell1.X To Cell2.X
            rect.Width += DataGridView1(i, Cell2.Y).Size.Width
        Next

        'セル位置の補正
        rect.X += 1
        rect.Y += 1

        'グラデーションをかけてヘッダーぽく見せる
        Dim gb As New System.Drawing.Drawing2D.LinearGradientBrush(rect, SystemColors.ControlLightLight, SystemColors.Control, System.Drawing.Drawing2D.LinearGradientMode.Vertical)
        gb.GammaCorrection = True

        '通常の塗りつぶし
        'e.Graphics.FillRectangle(New SolidBrush(SystemColors.Control), rect)
        e.Graphics.FillRectangle(gb, rect)
        e.Graphics.DrawRectangle(New Pen(DataGridView1.GridColor), rect)

        gb.Dispose()

        '描画するセル位置の文字をヘッダーテキストとして表示
        Dim headerText As String = DataGridView1(e.ColumnIndex, e.RowIndex).Value
        TextRenderer.DrawText(e.Graphics, headerText, e.CellStyle.Font, rect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

        e.Handled = True

    End If

End Sub

f:id:ssCARO:20200630145615p:plain