DataGridViewで行ヘッダーセルの結合を行う

VisualBasicのDataGridViewで行ヘッダーセルを左右2段にし、左側は複数セル分を結合して表示します。
文章では分かりにくいですが、下のようなDataGridViewを作ります。
列ヘッダを結合するコードは見つけたのですが、行ヘッダを結合する必要があったので作成してみました。
Visual Basic 2005 Express で作成しました。





Form1

フォームを1つ作成してDataGridView1を1個配置します。
コードビューを開き
 Form1 -> InitializeComponent を選択して Form1.Designer.vb を開きます
 (または ソリューションエクスプローラーですべてのファイルを表示を押して Form1.Designer.vb を開きます)

 Me.DataGridView1 = New System.Windows.Forms.DataGridView
           ↓
 Me.DataGridView1 = New ExVDataGridView

 Friend WithEvents DataGridView1 As System.Windows.Forms.DataGridView
           ↓
 Friend WithEvents DataGridView1 As ExVDataGridView
のように変更します。

注:この前に下記のExVDataGridView.vbを作っておいてください。無いとエラーになります。

Form1.vb
Public Class Form1

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'Form1 -> InitializeComponent 開いて
        '
        'Me.DataGridView1 = New System.Windows.Forms.DataGridView
        '      ↓
        'Me.DataGridView1 = New ExVDataGridView
        '
        'Friend WithEvents DataGridView1 As System.Windows.Forms.DataGridView
        '      ↓
        'Friend WithEvents DataGridView1 As ExVDataGridView

        DataGridView1.Columns.Add("aa", "AA")
        DataGridView1.Columns.Add("bb", "BB")
        DataGridView1.Columns.Add("cc", "CC")
        DataGridView1.Columns.Add("dd", "DD")
        DataGridView1.Columns(0).Width = 40
        DataGridView1.Columns(1).Width = 40
        DataGridView1.Columns(2).Width = 40
        DataGridView1.Columns(3).Width = 40

        Dim GL As New Generic.List(Of Integer)
        GL.Add(2)
        GL.Add(0)
        GL.Add(1)
        GL.Add(3)
        GL.Add(0)
        GL.Add(0)
        DataGridView1.ドッキングセル = GL
        Dim text() As String = {"横書き", "", "", "縦書き", "", ""}
        DataGridView1.FullHedTxt = text
        Dim hcolor() As String = {"AQUA", "", "", "red", "", ""}
        DataGridView1.FullHedColor = hcolor
        Dim hvertical() As Integer = {0, 0, 0, 1, 0, 0}
        DataGridView1.FullHedVerticalFlag = hvertical
        DataGridView1.RowHeadersWidth = 150
        DataGridView1.RowHeaderWidthR = 40
        DataGridView1.Rows.Add(10)
        DataGridView1.Rows(0).HeaderCell.Value = "1"
        DataGridView1.Rows(1).HeaderCell.Value = "22"
        DataGridView1.Rows(2).HeaderCell.Value = "333"
        DataGridView1.Rows(3).HeaderCell.Value = "4444"
        DataGridView1.Rows(4).HeaderCell.Value = "55555"
        DataGridView1.Rows(5).HeaderCell.Value = "6666"
        DataGridView1.Rows(6).HeaderCell.Value = "777"
        DataGridView1.Rows(7).HeaderCell.Value = "88"
        DataGridView1.Rows(8).HeaderCell.Value = "9"
        DataGridView1.Rows(9).HeaderCell.Value = "10"
        DataGridView1.Rows(0).HeaderCell.Style.BackColor = Color.Aqua
        DataGridView1.Rows(4).HeaderCell.Style.BackColor = Color.Yellow
        DataGridView1.Rows(8).HeaderCell.Style.BackColor = Color.LightPink

    End Sub


End Class






ExVDataGridViewクラス

下記サイトの情報を参考にクラスを作成しました。
インターフェースはほぼそのままですが、中身はほとんど原型を留めていません。
左右分割の割合指定とカラムの色指定、文字列の縦書き指定もできるようにしました。
これらの指定は元サイトに従って配列で指定します。
行の結合の指定方法は、
結合を始める行に何行結合するかを書き、それ以外はゼロにします。
1列目から2列分、3列目から1列分、4列目から3列分結合であれば
 2、0、1、3と設定します。この設定の場合7行目以降は普通のヘッダになります。
ヘッダ左側のテキストの指定方法は、
1行目から2行分結合した部分のテキストを"A"4行目から3行分結合させた部分のテキストを"B"とする場合
 A、空白、空白、B と設定します。空白の場合は何も表示されません。
結合開始行以外に書かれた文字列は無視されます。
ヘッダ右側に表示されるテキストは、DataGridViewに指定されているものが表示されます。
 DataGridView1.Rows(0).HeaderCell.Value = "1" のように指定してください。
ヘッダ左側のヘッダ色の指定方法は、
1行目から2行分結合した部分を水色、4行目から3行分結合させた部分を赤色とする場合
 Aqua、空白、空白、Red というふうに設定します。空白の場合デフォルト背景色が使用されます。
ヘッダ右側の色は、DataGridViewに指定されているものが表示されます。
 DataGridView1.Rows(0).HeaderCell.Style.BackColor = Color.Aqua のように指定してください。
ヘッダ左側の文字列の縦書き横書きの指定方法は、
1行目から2行分結合した部分は横書き、4行目から3行分結合させた部分を縦書きとする場合
 0、0、0、1 というふうに設定します。1を指定した部分が縦書きになります。
通常は何も指定しなければ横書きになります。
ヘッダ左右分割の割合の指定方法は、
左側の割合を%で指定します。左側40% 右側60%なら、
 DataGridView1.RowHeaderWidthsR = 40
と指定します。
実際のコーディングは上のForm1を参照してください。



ExVDataGridView.vb
'DataGridViewの2段表示
'http://social.msdn.microsoft.com/Forums/ja-JP/d2250a20-404d-4471-b07e-8a0d8f587c2e/datagridview2
'から改変、行ヘッダを2列表示にする 2013.10 T.F
Imports System.Windows.Forms
Imports System.Drawing
Public Class ExVDataGridView
    Inherits DataGridView

    Public Sub New()
        MyBase.new()
    End Sub
    Public pBorderColor As Color = SystemColors.ControlDark
    <System.ComponentModel.Category("カスタム")> _
    <System.ComponentModel.Description("境界線のカラー")> _
    Public Property BorderColor() As Color
        Get
            Return pBorderColor
        End Get
        Set(ByVal value As Color)
            pBorderColor = value
        End Set
    End Property

    Public pDokCell As New Generic.List(Of Integer)
    <System.ComponentModel.Category("カスタム")> _
    <System.ComponentModel.Description("結合するヘッダーを設定する値 設定する値は1列目から2列分、4列目から3列分結合であれば 2,0,0,3というふうに設定  Generic.List型 ")> _
    Public Property ドッキングセル() As Generic.List(Of Integer)
        Get
            Return pDokCell
        End Get
        Set(ByVal value As Generic.List(Of Integer))
            pDokCell = value
        End Set
    End Property

    Public pHedTXT() As String
    <System.ComponentModel.Category("カスタム")> _
    <System.ComponentModel.Description("行ヘッダーテキストです 1行目から2行分結合した部分のテキストを""A""4行目から3行分結合させた部分のテキストを""B""とする場合 A、空白、空白、B というふうに設定" & _
                                      " 空白に設定している列は何も表示されない")> _
    Public Property FullHedTxt() As String()
        Get
            Return pHedTXT
        End Get
        Set(ByVal value As String())
            pHedTXT = value
        End Set
    End Property
    Public RowHeaderWidthR As Integer = 50
    <System.ComponentModel.Category("カスタム")> _
    <System.ComponentModel.Description("左ヘッダの割合を%で設定")> _
    Public Property RowHeaderWidthRate() As Integer
        Get
            Return RowHeaderWidthR
        End Get
        Set(ByVal value As Integer)
            RowHeaderWidthR = value
        End Set
    End Property
    Public pHedColor() As String
    <System.ComponentModel.Category("カスタム")> _
    <System.ComponentModel.Description("上部ヘッダー背景色です 色名(RED、BLUEなど)かRGB値(#123456など)で指定します" & _
                                        "行ヘッダテキストの書いてあるのと同じ位置に書きます")> _
    Public Property FullHedColor() As String()
        Get
            Return pHedColor
        End Get
        Set(ByVal value As String())
            pHedColor = value
        End Set
    End Property
    Public pHedVerticalFlag() As Integer
    <System.ComponentModel.Category("カスタム")> _
    <System.ComponentModel.Description("上部ヘッダー縦書きフラグです 1で縦書き0で横書きです" & _
                                        "行ヘッダテキストの書いてあるのと同じ位置に書きます")> _
    Public Property FullHedVerticalFlag() As Integer()
        Get
            Return pHedVerticalFlag
        End Get
        Set(ByVal value As Integer())
            pHedVerticalFlag = value
        End Set
    End Property

    Private Sub Me_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles Me.CellPainting
        ヘッダー結合2(e, pDokCell)
    End Sub

    'セルの高さを変更した場合上側のセルの左部分にごみが残ることがあるのでDataGridViewを再描画する
    Private Sub DataGridView1_RowHeightChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowEventArgs) Handles Me.RowHeightChanged
        Dim r As Integer = e.Row.Index
        Dim dgv As DataGridView = CType(sender, DataGridView)
        dgv.Invalidate()
    End Sub



    Private Sub ヘッダー結合2(ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs, _
            ByVal Dokcell As Generic.List(Of Integer))

        If Dokcell Is Nothing Then Exit Sub


        If e.RowIndex >= 0 AndAlso e.ColumnIndex = -1 Then
            Dim StartRow As Integer '結合開始行 結合しない場合は-1を入れる
            Dim EndRow As Integer '結合終了行 結合しない場合は-1を入れる
            '左右に分かれるセルか繋がったセルかを判別

            Dim row As Integer = e.RowIndex
            Dim DokFlg As New ArrayList
            Dim startar(Me.Rows.Count) As Integer '結合スタートセル位置保存用配列
            Dim endar(Me.Rows.Count) As Integer '結合エンドセル位置保存用配列
            If row > Dokcell.Count - 1 Then '設定範囲以上=結合しないセル
                StartRow = -1
                EndRow = -1
            ElseIf Dokcell(row) > 0 Then '結合セルの先頭
                StartRow = row
                EndRow = row + Dokcell(row) - 1
            Else '結合セルの2番目以降か結合しないセル
                For iii As Integer = 0 To CInt(Dokcell.Count) - 1 '結合セルセルをリストアップ
                    If Dokcell(iii) > 0 Then
                        DokFlg.Add(iii)
                        startar(iii) = iii
                        endar(iii) = iii + Dokcell(iii) - 1
                        For ii As Integer = iii To iii + Dokcell(iii) - 1
                            DokFlg.Add(ii)
                            startar(ii) = iii
                            endar(ii) = iii + Dokcell(iii) - 1
                        Next
                    End If
                Next
                If DokFlg.Contains(row) = False Then '結合しないセル
                    StartRow = -1
                    EndRow = -1
                Else '結合するセルの2番目以降
                    StartRow = startar(row)
                    EndRow = endar(row)
                End If
            End If



            Dim BoPen As New Pen(BorderColor)
            Dim sf As New StringFormat() '文字列を中央そろえに設定
            sf.Alignment = StringAlignment.Center
            sf.LineAlignment = StringAlignment.Center
            Try
                If StartRow = -1 Then '結合しないセル--------------------------------

                    Dim Br As New SolidBrush(e.CellStyle.BackColor) '背景色を取得してブラシを作成
                    e.Graphics.FillRectangle(Br, e.CellBounds) '塗る
                    Br.Dispose()
                    Dim x2 As Integer = e.CellBounds.X
                    Dim Y2 As Integer = e.CellBounds.Y
                    Dim hei As Integer = e.CellBounds.Height
                    Dim wid As Integer = CInt(e.CellBounds.Width)
                    Dim txt As String = CStr(e.Value)
                    Dim Bou1 As Rectangle = New Rectangle(x2, Y2, wid - 1, hei - 1) '枠線表示
                    e.Graphics.DrawRectangle(BoPen, Bou1)
                    e.Graphics.DrawString(txt, e.CellStyle.Font, Brushes.Black, Bou1, sf)
                    e.Graphics.DrawLine(Pens.White, x2 + 1, Y2, x2 + 1, Y2 + hei - 1) '左のハイライト
                    e.Graphics.DrawLine(Pens.White, x2 + 1, Y2, x2 + wid - 2, Y2) '上のハイライト
                    e.Handled = True
                Else '結合するセル--------------------------------

                    Dim x2 As Integer = e.CellBounds.X
                    Dim Y2 As Integer = e.CellBounds.Y
                    Dim hei As Integer = e.CellBounds.Height
                    Dim wid As Integer = CInt(e.CellBounds.Width)
                    Dim widL As Integer = wid * RowHeaderWidthR / 100
                    Dim widR As Integer = wid - widL

                    '右側のセル--------------
                    Dim Bou1 As Rectangle = New Rectangle(x2 + widL, Y2, widR - 1, hei - 1) '表示矩形

                    Dim Br As New SolidBrush(e.CellStyle.BackColor) '背景色を取得してブラシを作成
                    e.Graphics.FillRectangle(Br, Bou1) '塗る
                    Br.Dispose()
                    e.Graphics.DrawRectangle(BoPen, Bou1) '枠線

                    Dim txt As String = CStr(e.Value)
                    e.Graphics.DrawString(txt, e.CellStyle.Font, Brushes.Black, Bou1, sf) '文字列表示
                    e.Graphics.DrawLine(Pens.White, x2 + widL, Y2, x2 + widL, Y2 + hei - 1) '左のハイライト
                    e.Graphics.DrawLine(Pens.White, x2 + widL, Y2, x2 + wid - 2, Y2) '上のハイライト

                    '左側のセル--------------

                    Dim hei2 As Integer = 0 '高さ
                    For i As Integer = StartRow To EndRow
                        hei2 = hei2 + Me.Rows(i).Height
                    Next
                    Dim hei3 As Integer = 0 '現在のセルより上にあるセルの高さ
                    For i As Integer = StartRow To row - 1
                        hei3 = hei3 + Me.Rows(i).Height
                    Next
                    Dim Bou2 As Rectangle = New Rectangle(x2, Y2 - hei3, widL - 1, hei2 - 1) '表示矩形

                    Dim Br2 As SolidBrush
                    If Not pHedColor Is Nothing AndAlso pHedColor.Length > StartRow AndAlso pHedColor(StartRow) <> "" Then
                        Dim cl As String = IsColorEx(pHedColor(StartRow))
                        If cl <> "" Then '色を表す文字列
                            Br2 = New SolidBrush(ColorTranslator.FromHtml(cl)) 'ブラシを作成
                        Else '色として認識できない記述
                            Br2 = New SolidBrush(Me.RowHeadersDefaultCellStyle.BackColor) '背景色を取得してブラシを作成
                        End If
                    Else
                        Br2 = New SolidBrush(Me.RowHeadersDefaultCellStyle.BackColor) '背景色を取得してブラシを作成
                    End If

                    txt = pHedTXT(StartRow) '文字列を得る
                    If Not pHedVerticalFlag Is Nothing Then '縦書きフラグをチェック
                        If pHedVerticalFlag.Length > StartRow Then
                            If pHedVerticalFlag(StartRow) = 1 Then
                                sf.FormatFlags = StringFormatFlags.DirectionVertical
                            End If
                        End If
                    End If

                    If Not Me.FirstDisplayedCell Is Nothing Then
                        Dim t As Integer = Me.FirstDisplayedCell.RowIndex
                        If t > StartRow Then '結合セル上方が表示範囲から上にはみ出す
                            e.Graphics.SetClip(New Rectangle(Me.Left, Me.ColumnHeadersHeight + 1, Me.Width, Me.Height))
                        Else '結合セル上方は表示範囲内
                        End If
                    End If

                    e.Graphics.FillRectangle(Br2, Bou2) '塗る
                    e.Graphics.DrawRectangle(BoPen, Bou2) '枠線
                    e.Graphics.DrawString(txt, e.CellStyle.Font, Brushes.Black, Bou2, sf) '文字列表示

                    Br2.Dispose()

                    e.Graphics.DrawLine(Pens.White, x2 + 1, Y2 - hei3, x2 + 1, Y2 + hei2 - 1) '左のハイライト
                    e.Graphics.DrawLine(Pens.White, x2 + 1, Y2 - hei3, x2 + widL - 2, Y2 - hei3) '上のハイライト

                    e.Handled = True
                    End If
            Finally

                BoPen.Dispose()
                sf.Dispose()

            End Try


        End If

    End Sub


    '色を表す文字列をチェック、ウィンドウ登録の文字列と数値0〜0xfffffならその文字列を返す
    '認識できなかったらNULL文字を返す
    Dim IsColorExColorName() As String
    Dim IsColorExcomp As StringComparer
    Private Function IsColorEx(ByVal s As String) As String
        If IsColorExColorName Is Nothing Then
            IsColorExColorName = [Enum].GetNames(GetType(KnownColor)) '色を表す文字列を取得
            IsColorExcomp = StringComparer.InvariantCultureIgnoreCase
            Array.Sort(IsColorExColorName, IsColorExcomp)
        End If
        s = s.Trim()
        If s.Length = 0 Then Return ""
        Dim ix As Integer = Array.BinarySearch(IsColorExColorName, s, IsColorExcomp) '大文字小文字を区別せず検索
        If ix >= 0 Then '色を表す文字列
            Return IsColorExColorName(ix)
        End If

        Try
            ix = -1
            If System.Text.RegularExpressions.Regex.IsMatch(s, "^[0-9]*$") = True Then
                ix = CInt(s)
                If ix >= 0 And ix <= &HFFFFFF Then
                    IsColorEx = "0x" + ix.ToString("x6")
                    Return IsColorEx
                Else
                    Return ""
                End If
            ElseIf s.IndexOf("0x") = 0 Or s.IndexOf("0X") = 0 Or s.IndexOf("&h") = 0 Or s.IndexOf("&H") = 0 Then
                s = s.Substring(2)
            ElseIf s.IndexOf("#") = 0 Then
                s = s.Substring(1)
            End If
            If s.Length = 0 Then Return ""
            If System.Text.RegularExpressions.Regex.IsMatch(s, "^[0-9A-Fa-f]*$") = True Then
                ix = Integer.Parse(s, System.Globalization.NumberStyles.HexNumber)
            End If
            If ix >= 0 And ix <= &HFFFFFF Then
                IsColorEx = "0x" + ix.ToString("x6")
                Return IsColorEx
            Else
                Return ""
            End If
        Catch ex As Exception
            Return ""
        End Try


    End Function




End Class







参考
DataGridViewの2段表示





TOPに戻る
2013/10/4