2013年8月20日火曜日

【VB.NET】ComboBoxのリストに複数の列を表示する

AccessのComboBoxのようにVB.NETのComboBoxのリストに複数の列を表示します。
複数の列の表示はComboBoxのDataSourceがDataTableの場合に限ります。


先ずプロジェクトに表示する列を定義するコレクションのためのDropDownColumnクラスを作ります。
Imports System.Drawing
Imports System.ComponentModel
 
''' <summary>
''' ドロップダウンリストに表示される列の定義
''' </summary>
''' <remarks></remarks>
Public Class DropDownColumn
 
    ''' <summary>
    ''' 列のプロパティ
    ''' </summary>
    ''' <remarks></remarks>
    Private _member As String
 
    ''' <summary>
    ''' 列のプロパティを設定または取得します。
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Description("列のプロパティ")>
    Public Property Member As String
        Get
            Return _member
        End Get
        Set(ByVal value As String)
            _member = value
        End Set
    End Property
 
    ''' <summary>
    ''' 列の幅
    ''' </summary>
    ''' <remarks></remarks>
    Private _width As Integer = Nothing
 
    ''' <summary>
    ''' 列の幅を設定または取得します。
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Description("列の幅")>
    Public Property Width As Integer
        Get
            Return _width
        End Get
        Set(ByVal value As Integer)
            _width = value
        End Set
    End Property
 
    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
        'インスタンスの初期化
    End Sub
 
    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    ''' <param name="paramMember">表示する列名</param>
    ''' <param name="paramWidth">幅</param>
    ''' <remarks>ドロップダウンリストに表示される列の定義</remarks>
    Public Sub New(ByVal paramMember As String, ByVal paramWidth As Integer)
        Member = paramMember
        Width = paramWidth
    End Sub
End Class

次にComboBoxの継承クラスをプロジェクトに作成します。
DrawItemイベントで列の境界線を描画しDataSourceに連結されたDataTableから列の値を取得して表示します。
selectMemberValueプロパティでは選択されている行の他の列の値を取得します。
Imports System.Windows.Forms
Imports System.Drawing
Imports System.ComponentModel
 
Public Class CustomDropDownComboBox
    Inherits System.Windows.Forms.ComboBox
 
    Private _item As New MyCollection(Me)
 
    ''' <summary>
    ''' ドロップダウンリストの列を設定します。
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
    <Category("ドロップダウンリストの列のカスタマイズ")> _
    <Description("ドロップダウンリストの列を設定します")>
    Public Property DropDownColumns() As MyCollection
        Get
            Return _item
        End Get
        Set(ByVal value As MyCollection)
            _item = value
        End Set
    End Property
 
    ''' <summary>
    ''' ドロップダウンリストの列コレクションが変更された場合。
    ''' </summary>
    ''' <remarks></remarks>
    Friend Sub OnCollectionChanged()
 
        '列の幅の合計をドロップダウン部分の幅に設定。
        If DropDownColumns.Count > 0 Then
            MyBase.DropDownWidth = DropDownColumns(0).Width
 
            For i = 1 To DropDownColumns.Count - 1
                MyBase.DropDownWidth += DropDownColumns(i).Width
            Next
        End If
    End Sub
 
    ''' <summary>
    ''' ドロップダウンリストの列コレクションの設定。
    ''' </summary>
    ''' <remarks></remarks>
    Public Class MyCollection
        Inherits System.Collections.ObjectModel.Collection(Of DropDownColumn)
 
        Private _parent As CustomDropDownComboBox
 
        Friend Sub New(ByVal parent As CustomDropDownComboBox)
            _parent = parent
        End Sub
 
        Protected Overrides Sub ClearItems()
            MyBase.ClearItems()
            _parent.OnCollectionChanged()
        End Sub
 
        Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As DropDownColumn)
 
            '列の幅の初期値
            If item.Width = Nothing Then
                item.Width = _parent.Width
            End If
 
            MyBase.InsertItem(index, item)
            _parent.OnCollectionChanged()
 
        End Sub
 
        Protected Overrides Sub RemoveItem(ByVal index As Integer)
            MyBase.RemoveItem(index)
            _parent.OnCollectionChanged()
        End Sub
 
        Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As DropDownColumn)
            MyBase.SetItem(index, item)
            _parent.OnCollectionChanged()
        End Sub
    End Class
 
    ''' <summary>
    ''' 現在選択されている行のメンバーの値を取得します。
    ''' </summary>
    ''' <param name="member">メンバー</param>
    ''' <value></value>
    ''' <returns>選択されている値</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property selectMemberValue(ByVal member As String)
        Get
            'データソースがない場合はNothingを返す。
            If MyBase.DataSource Is Nothing Then
                Return Nothing
            End If
 
            'データソースがデータテーブルでない場合はNothingを返す。
            If MyBase.DataSource.GetType IsNot GetType(DataTable) Then
                Return Nothing
            End If
 
            'DataTableを取得
            Dim dt As DataTable = CType(Me.DataSource, DataTable)
 
            'メンバーがデータテーブルの列でない場合はNothingを返す。
            If dt.Columns.IndexOf(member) = -1 Then
                Return Nothing
            End If
 
            'メンバーがデータテーブルの列の場合値を返す。
            Return dt.Rows(Me.SelectedIndex).Item(member)
 
        End Get
    End Property
 
    ''' <summary>
    ''' 描画処理
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)
        MyBase.OnDrawItem(e)
 
        '選択されているアイテムのハイライト
        e.DrawBackground()
        e.DrawFocusRectangle()
 
        'データソースがない場合は除外。
        If MyBase.DataSource Is Nothing Then
            Exit Sub
        End If
 
        'データソースがデータテーブルでない場合は除外
        If MyBase.DataSource.GetType IsNot GetType(DataTable) Then
            Exit Sub
        End If
 
        'データテーブルの取得
        Dim dt As DataTable = CType(MyBase.DataSource, DataTable)
 
        'アイテムの描画座標の取得
        Dim x As Integer = e.Bounds.X
        Dim y As Integer = e.Bounds.Y
        Dim width As Integer = e.Bounds.X
        Dim height As Integer = e.Bounds.Height
        Dim top As Integer = e.Bounds.Top
        Dim bottom As Integer = e.Bounds.Bottom
 
        '色の取得
        Dim grayPen As New Pen(Brushes.Gray)
        Dim brsh As New SolidBrush(e.ForeColor)
 
        '文字列の表示形式
        Dim sf As New StringFormat
        sf.Trimming = StringTrimming.EllipsisWord
 
        Try
            For i = 0 To Me.DropDownColumns.Count - 1
 
                '列の幅の取得
                width = Me.DropDownColumns(i).Width
 
                '列の境界線の描画
                e.Graphics.DrawLine(grayPen, x + width, top, x + width, bottom)
 
                '列のメンバーの取得
                Dim columnIndex As String = Me.DropDownColumns(i).Member
 
                '列のメンバーがない場合またはデータテーブルの列でない場合は除外
                If columnIndex Is Nothing OrElse columnIndex = String.Empty _
                    OrElse dt.Columns.IndexOf(columnIndex) = -1 Then
                    x += width
                    Continue For
                End If
 
                '列に表示する値を取得
                Dim text As String = dt.Rows(e.Index)(columnIndex).ToString
                '描画領域
                Dim rectF As New RectangleF(CSng(x), CSng(y), CSng(width), CSng(height))
                '値の描画
                e.Graphics.DrawString(text, e.Font, brsh, rectF, sf)
 
                '次の列の座標
                x += width
 
            Next
        Finally
            'リソースの解放
            grayPen.Dispose()
            brsh.Dispose()
            sf.Dispose()
        End Try
        
    End Sub
 
End Class


フォームに配置しての使用例

DrawModeをOwnerDrawFixedにして使用します。またDropDownStyleのSimpleには対応していません。

Imports System.Reflection
Imports System.Windows.Forms.ComboBox
 
Public Class Form1
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
 
        'サンプルデータテーブル
        Dim dt As New DataTable
        dt.Columns.Add("ID", Type.GetType("System.String"))
        dt.Columns.Add("NAME", Type.GetType("System.String"))
        dt.Columns.Add("ADDR", Type.GetType("System.String"))
 
        dt.Rows.Add("001", "名前001", "住所001")
        dt.Rows.Add("002", "名前002", "住所002")
        dt.Rows.Add("003", "名前003", "住所003")
        dt.Rows.Add("004", "名前004", "住所004")
        dt.Rows.Add("005", "名前005", "住所005")
        dt.Rows.Add("006", "名前006", "住所006")
        dt.Rows.Add("007", "名前007", "住所007")
        dt.Rows.Add("008", "名前008", "住所008")
        dt.Rows.Add("009", "名前009", "住所009")
        dt.Rows.Add("009", "名前009", "住所009")
 
        
        'CustomDropDownComboBoxの設定
        Me.CustomDropDownComboBox1.DataSource = dt
        Me.CustomDropDownComboBox1.DisplayMember = "NAME"
        Me.CustomDropDownComboBox1.ValueMember = "ID"
 
        'ドロップダウンリストの列の追加
        Dim cols As New DropDownColumn
        cols.Member = "ID"
        cols.Width = 50
        Me.CustomDropDownComboBox1.DropDownColumns.Add(cols)
 
        cols = New DropDownColumn
        cols.Member = "NAME"
        cols.Width = 100
        Me.CustomDropDownComboBox1.DropDownColumns.Add(cols)
 
        cols = New DropDownColumn("ADDR", 100)
        Me.CustomDropDownComboBox1.DropDownColumns.Add(cols)
 
        '描画モード
        Me.CustomDropDownComboBox1.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
 
    End Sub
End Class
実行結果
(注意)
DataSourceがDataTableでない場合は列は表示されません。
表示しようとする列がDataTableに無い場合の表示はされません。

2013年8月8日木曜日

【VB.NET】DataGridViewの列ヘッダーの複数行表示するカスタムコントロール②


前回の記事で作成したCustomDataGridViewの使用例です。

プロジェクトにフォームを追加しツールボックスからCustomDataGridViewをフォームに配置します。

先ずはColumnHeaderHeigthSizeModeプロパティをAutoSize以外に設定します。
次に新たに追加したプロパティを設定します。

HeaderCellプロパティをクリックするとHeaderCellコレクションエディターが開くので
HeaderCellを追加設定します。
OKボタンをクリックするとHeaderCellの設定がフォームデザイナーのCustomDataGridViewに
反映されます。

実行結果

HeaderCellは並び順に上書きされます。


またフォームにCustomDataGridviewを配置しコードからプロパティの設定もできます。



Imports System.Reflection
Imports System.Windows.Forms.ComboBox

Public Class Form1
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        'サンプルデータテーブル
        Dim dt As New DataTable
        dt.Columns.Add("NO", Type.GetType("System.Double"))
        dt.Columns.Add("CATEGORY1", Type.GetType("System.String"))
        dt.Columns.Add("CATEGORY2", Type.GetType("System.String"))
        dt.Columns.Add("CATEGORY3", Type.GetType("System.String"))
        dt.Columns.Add("NAME", Type.GetType("System.String"))
        dt.Columns.Add("VAL1", Type.GetType("System.Double"))
        dt.Columns.Add("VAL2", Type.GetType("System.Double"))
        dt.Columns.Add("VAL3", Type.GetType("System.Double"))
        dt.Columns.Add("VAL4", Type.GetType("System.Double"))
        dt.Columns.Add("VAL5", Type.GetType("System.Double"))

        Dim r As New System.Random(1000)

        Dim no As Integer = 0
        For j1 = 0 To 100
            For j2 = 0 To 5
                For j3 = 0 To 3
                    Dim nr As DataRow = dt.NewRow
                    nr("NO") = no
                    nr("CATEGORY1") = "分類" & j1.ToString("00")
                    nr("CATEGORY2") = "分類" & j2.ToString("00")
                    nr("CATEGORY3") = "分類" & j3.ToString("00")
                    nr("NAME") = "名前" & j1.ToString("00") & j2.ToString("00") & j3.ToString("00")
                    nr("VAL1") = r.Next(10000)
                    nr("VAL2") = r.Next(10000)
                    nr("VAL3") = r.Next(10000)
                    nr("VAL4") = r.Next(10000)
                    nr("VAL5") = r.Next(10000)
                    dt.Rows.Add(nr)

                    no += 1
                Next
            Next
        Next

        
        'CustomHeaderDataGridView1の設定
        Me.CustomHeaderDataGridView1.DataSource = dt

        Me.CustomHeaderDataGridView1.ColumnHeaderBorderStyle = CustomHeaderDataGridView.HeaderCellBorderStyle.DoubleLine
        Me.CustomHeaderDataGridView1.ColumnHeaderRowCount = 2
        Me.CustomHeaderDataGridView1.ColumnHeaderRowHeight = 17

        'ヘッダーセル定義
        Dim cells As New HeaderCell
        cells.Column = 0
        cells.Row = 0
        cells.ColumnSpan = 1
        cells.RowSpan = 2
        cells.SortVisible = True
        cells.Text = "NO"
        cells.TextAlign = DataGridViewContentAlignment.MiddleCenter
        Me.CustomHeaderDataGridView1.HeaderCells.Add(cells)

        cells = New HeaderCell
        cells.Column = 1
        cells.Row = 0
        cells.ColumnSpan = 3
        cells.RowSpan = 1
        cells.SortVisible = True
        cells.Text = "CATEGORY"
        cells.TextAlign = DataGridViewContentAlignment.MiddleCenter
        Me.CustomHeaderDataGridView1.HeaderCells.Add(cells)

        cells = New HeaderCell
        cells.Column = 4
        cells.Row = 0
        cells.ColumnSpan = 1
        cells.RowSpan = 2
        cells.SortVisible = True
        cells.Text = "NAME"
        cells.TextAlign = DataGridViewContentAlignment.MiddleCenter
        Me.CustomHeaderDataGridView1.HeaderCells.Add(cells)

        cells = New HeaderCell
        cells.Column = 5
        cells.Row = 0
        cells.ColumnSpan = 5
        cells.RowSpan = 1
        cells.SortVisible = True
        cells.Text = "VAL"
        cells.TextAlign = DataGridViewContentAlignment.MiddleCenter
        Me.CustomHeaderDataGridView1.HeaderCells.Add(cells)

        'ヘッダーサイズモード
        Me.CustomHeaderDataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing
    End Sub
End Class
実行結果
(注意点) 
ColumnHeadersHeightSizeModeは手動でAutoSize以外に変更する必要があります。
四角形と文字を描画しているだけなので結合されたセル上でマウスクリックされた場合はその直下の列がソートされます。

2013年8月4日日曜日

【VB.NET ・C#】DataGridViewの列ヘッダーの複数行表示するカスタムコントロール①

DataGridViewを継承して列ヘッダーの複数行表示をするカスタムコントロールをつくります。

先ずは列ヘッダーのセルの結合のための定義をコレクションエディターで表示するためにHeaderCellクラスをプロジェクトに追加します。ヘッダーセル定義は以下の項目にします。
項目意味
Column
Row
ColumnSpan結合する列数
RowSpan結合する行数
BackgroundColorセルの背景色
ForeColorセルの文字色
SortVisible結合したセル内にソートされた列がある場合のソート方向の表示の有無
Textセルに関連付けられたテキスト
TextAlign結合されたセル内のテキストの位置

VB.NET
Imports System
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing

'Public Class HeaderCellColection
'    Inherits System.Collections.Generic.List(Of HeaderCell)
'End Class

''' <summary>
''' ヘッダーセル定義
''' </summary>
''' <remarks></remarks>
Public Class HeaderCell

    Private _row As Integer
    ''' <summary>
    ''' 行
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("セル位置")> _
    <Description("行")>
    Public Property Row As Integer
        Get
            Return _row
        End Get
        Set(ByVal value As Integer)
            _row = value
        End Set
    End Property

    Private _column As Integer
    ''' <summary>
    ''' 列
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("セル位置")> _
    <Description("列")>
    Public Property Column As Integer
        Get
            Return _column
        End Get
        Set(ByVal value As Integer)
            _column = value
        End Set
    End Property

    Private _rowSpan As Integer = 1
    ''' <summary>
    ''' 結合する行数
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("セル結合")> _
    <Description("行数")>
    Public Property RowSpan As Integer
        Get
            Return _rowSpan
        End Get
        Set(ByVal value As Integer)
            _rowSpan = value
        End Set
    End Property

    Private _columnSpan As Integer = 1
    ''' <summary>
    ''' 結合する列数
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("セル結合")> _
    <Description("列数")>
    Public Property ColumnSpan As Integer
        Get
            Return _columnSpan
        End Get
        Set(ByVal value As Integer)
            _columnSpan = value
        End Set
    End Property

    Private _backgroundColor As System.Drawing.Color = Color.Empty
    ''' <summary>
    ''' セルの背景色
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("表示")> _
    <Description("セルの背景色")>
    Public Property BackgroundColor As System.Drawing.Color
        Get
            Return _backgroundColor
        End Get
        Set(ByVal value As System.Drawing.Color)
            _backgroundColor = value
        End Set
    End Property

    Private _foreColor As System.Drawing.Color = Color.Empty
    ''' <summary>
    ''' テキストの文字色
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("表示")> _
    <Description("テキストの文字色")>
    Public Property ForeColor As System.Drawing.Color
        Get
            Return _foreColor
        End Get
        Set(ByVal value As System.Drawing.Color)
            _foreColor = value
        End Set
    End Property


    Private _text As String
    ''' <summary>
    ''' セルに関連付けられたテキスト
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("表示")> _
    <Description("セルに関連付けられたテキストです")>
    Public Property Text As String
        Get
            Return _text
        End Get
        Set(ByVal value As String)
            _text = value
        End Set
    End Property

    Private _textAlign As DataGridViewContentAlignment
    ''' <summary>
    ''' 結合されたセル内でのテキストの位置
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("表示")> _
    <Description("結合されたセル内のテキストの位置を決定します")>
    Public Property TextAlign As DataGridViewContentAlignment
        Get
            Return _textAlign
        End Get
        Set(ByVal value As DataGridViewContentAlignment)
            _textAlign = value
        End Set
    End Property

    Private _wrapMode As DataGridViewTriState = DataGridViewTriState.NotSet
    ''' <summary>
    ''' セルに含まれるテキスト形式の内容が 1 行に収まらないほど長い場合に、次の行に折り返されるか、
    ''' 切り捨てられるかを示す値を取得または設定する
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("表示")> _
    <Description("セル内のテキストが一行に収まらない場合にテキストを折り返す")>
    Public Property WrapMode As DataGridViewTriState
        Get
            Return _wrapMode
        End Get
        Set(ByVal value As DataGridViewTriState)
            _wrapMode = value
        End Set
    End Property

    Private _sortVisible As Boolean
    ''' <summary>
    ''' 結合されている列に並び替えがある場合に並び替えの方向を表示する
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("表示")> _
    <Description("結合されている列に並び替えがある場合に並び替えの方向を表示する")>
    Public Property SortVisible As Boolean
        Get
            Return _sortVisible
        End Get
        Set(ByVal value As Boolean)
            _sortVisible = value
        End Set
    End Property
End Class
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;

namespace ClassLibrary1
{
    class HeaderCell

    {
        private int _row;
        /// 
        /// 行
        /// 
        /// 
        /// 
        /// 
        [Category("セル位置")]
        [Description("行")]
        public int Row
        {
            get { return _row; }
            set { _row = value; }
        }

        private int _column;
        /// 
        /// 列
        /// 
        /// 
        /// 
        /// 
        [Category("セル位置")]
        [Description("列")]
        public int Column
        {
            get { return _column; }
            set { _column = value; }
        }

        private int _rowSpan = 1;
        /// 
        /// 結合する行数
        /// 
        /// 
        /// 
        /// 
        [Category("セル結合")]
        [Description("行数")]
        public int RowSpan
        {
            get { return _rowSpan; }
            set { _rowSpan = value; }
        }

        private int _columnSpan = 1;
        /// 
        /// 結合する列数
        /// 
        /// 
        /// 
        /// 
        [Category("セル結合")]
        [Description("列数")]
        public int ColumnSpan
        {
            get { return _columnSpan; }
            set { _columnSpan = value; }
        }

        private System.Drawing.Color _backgroundColor = Color.Empty;
        /// 
        /// セルの背景色
        /// 
        /// 
        /// 
        /// 
        [Category("表示")]
        [Description("セルの背景色")]
        public System.Drawing.Color BackgroundColor
        {
            get { return _backgroundColor; }
            set { _backgroundColor = value; }
        }

        private System.Drawing.Color _foreColor = Color.Empty;
        /// 
        /// テキストの文字色
        /// 
        /// 
        /// 
        /// 
        [Category("表示")]
        [Description("テキストの文字色")]
        public System.Drawing.Color ForeColor
        {
            get { return _foreColor; }
            set { _foreColor = value; }
        }


        private string _text;
        /// 
        /// セルに関連付けられたテキスト
        /// 
        /// 
        /// 
        /// 
        [Category("表示")]
        [Description("セルに関連付けられたテキストです")]
        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        private DataGridViewContentAlignment _textAlign;
        /// 
        /// 結合されたセル内でのテキストの位置
        /// 
        /// 
        /// 
        /// 
        [Category("表示")]
        [Description("結合されたセル内のテキストの位置を決定します")]
        public DataGridViewContentAlignment TextAlign
        {
            get { return _textAlign; }
            set { _textAlign = value; }
        }

        private DataGridViewTriState _wrapMode = DataGridViewTriState.NotSet;
        /// 
        /// セルに含まれるテキスト形式の内容が 1 行に収まらないほど長い場合に、次の行に折り返されるか、
        /// 切り捨てられるかを示す値を取得または設定する
        /// 
        /// 
        /// 
        /// 
        [Category("表示")]
        [Description("セル内のテキストが一行に収まらない場合にテキストを折り返す")]
        public DataGridViewTriState WrapMode
        {
            get { return _wrapMode; }
            set { _wrapMode = value; }
        }

        private bool _sortVisible;
        /// 
        /// 結合されている列に並び替えがある場合に並び替えの方向を表示する
        /// 
        /// 
        /// 
        /// 
        [Category("表示")]
        [Description("結合されている列に並び替えがある場合に並び替えの方向を表示する")]
        public bool SortVisible
        {
            get { return _sortVisible; }
            set { _sortVisible = value; }
        }
    }
}

次にDataGridViewの継承クラスを作成します。

クラスに以下ののプロパティを追加。
プロパティ意味
ColumnHeaderBorderStyle列ヘッダーセルの境界線の線種
ColumnHeaderRowCount列ヘッダーの行数
ColumnHeaderRowHeight列ヘッダーの行の高さ
HeaderCells列ヘッダーのセル定義

次にOnPaintイベントにヘッダセルの描画処理とセルの結合の描画処理を記述します。

スクロール時、コントロールのサイズ変更時および列の幅が変更された時に直前の描画が
残るのを防ぐために描画領域の無効化をしています。
VB.NET
Imports System
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing
'Imports MyLibrary.HeaderCell


Public Class CustomHeaderDataGridView
    Inherits System.Windows.Forms.DataGridView

    Private _item As New MyCollection(Me)

    ''' <summary>
    ''' 列ヘッダに表示するCellを設定します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
    <Category("列ヘッダのカスタマイズ")> _
    <Description("列ヘッダに表示するCellを設定します")>
    Public ReadOnly Property HeaderCells() As MyCollection
        Get
            Return _item
        End Get
    End Property

    Friend Sub OnCollectionChanged()
        Me.Invalidate()
    End Sub



    ''' <summary>
    ''' コレクションの設定
    ''' </summary>
    ''' <remarks></remarks>
    Public Class MyCollection
        Inherits System.Collections.ObjectModel.Collection(Of HeaderCell)

        Private _parent As CustomHeaderDataGridView

        Friend Sub New(ByVal parent As CustomHeaderDataGridView)
            _parent = parent
        End Sub

        Protected Overrides Sub ClearItems()
            MyBase.ClearItems()
            _parent.OnCollectionChanged()
        End Sub

        Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As HeaderCell)
            MyBase.InsertItem(index, item)
            _parent.OnCollectionChanged()
        End Sub

        Protected Overrides Sub RemoveItem(ByVal index As Integer)
            MyBase.RemoveItem(index)
            _parent.OnCollectionChanged()
        End Sub

        Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As HeaderCell)
            MyBase.SetItem(index, item)
            _parent.OnCollectionChanged()
        End Sub

    End Class


    Private _columnHeaderRowCount As Integer = 1
    ''' <summary>
    ''' 列ヘッダーの行数を設定します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("列ヘッダのカスタマイズ")> _
    <Description("列ヘッダに表示する行を設定します")>
    Public Property ColumnHeaderRowCount As Integer
        Get
            Return _columnHeaderRowCount
        End Get
        Set(ByVal value As Integer)



            _columnHeaderRowCount = value

            If value = 0 Then
                _columnHeaderRowCount = 1
            End If

            MyBase.ColumnHeadersHeight = value * ColumnHeaderRowHeight + 2
            MyBase.Refresh()
        End Set
    End Property

    Private _columnHeaderRowHeight As Integer = 17
    ''' <summary>
    ''' 列ヘッダに表示する行の高さ
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("列ヘッダのカスタマイズ")> _
    <Description("列ヘッダに表示する行の高さを設定します")>
    Public Property ColumnHeaderRowHeight As Integer
        Get
            Return _columnHeaderRowHeight
        End Get
        Set(ByVal value As Integer)
            _columnHeaderRowHeight = value

            MyBase.ColumnHeadersHeight = value * ColumnHeaderRowCount + 2
            MyBase.Refresh()
        End Set
    End Property

    ''' <summary>
    ''' 列ヘッダーの境界線の種類
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum HeaderCellBorderStyle
        SingleLine = 0
        DoubleLine = 1
    End Enum

    Private _columnHeaderBorderStyle As HeaderCellBorderStyle = HeaderCellBorderStyle.SingleLine
    ''' <summary>
    ''' 列ヘッダーの線種
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("列ヘッダのカスタマイズ")> _
    <Description("列ヘッダに線種を設定します")>
    Public Property ColumnHeaderBorderStyle As HeaderCellBorderStyle
        Get
            Return _columnHeaderBorderStyle
        End Get
        Set(ByVal value As HeaderCellBorderStyle)
            _columnHeaderBorderStyle = value
            MyBase.Refresh()
        End Set
    End Property

    <System.Diagnostics.DebuggerNonUserCode()> _
    Public Sub New()
        MyBase.New()

        'この呼び出しは、コンポーネント デザイナーで必要です。
        InitializeComponent()

        MyBase.DoubleBuffered = True

    End Sub

    'Component は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'コンポーネント デザイナーで必要です。
    Private components As System.ComponentModel.IContainer

    'メモ: 以下のプロシージャはコンポーネント デザイナーで必要です。
    'コンポーネント デザイナーを使って変更できます。
    'コード エディターを使って変更しないでください。
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
    End Sub

    

    ''' <summary>
    ''' 再描画をするとき
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        Try

            
            '---------------------------------------------------------------------------------------------------------
            'ヘッダーセルの描画
            '---------------------------------------------------------------------------------------------------------

            'ヘッダーの行の高さの取得
            Dim rowHeight As Integer = MyBase.ColumnHeadersHeight

            If Me.ColumnHeaderRowCount > 0 Then
                rowHeight = MyBase.ColumnHeadersHeight / Me.ColumnHeaderRowCount
            End If

            '線の太さ
            Dim lineWidth As Integer = 1

            For i = 0 To ColumnCount - 1

                For j = 0 To Me.ColumnHeaderRowCount - 1

                    'グッリドの線
                    Dim gridPen As New Pen(MyBase.GridColor)

                    '背景色
                    Dim backBrash As New SolidBrush(MyBase.ColumnHeadersDefaultCellStyle.BackColor)

                    'くぼみ線
                    Dim whiteBrash As New SolidBrush(Color.White)

                    Try
                        '列ヘッダーの描画領域
                        Dim rect As Rectangle = MyBase.GetCellDisplayRectangle(i, -1, True)

                        '列ヘッダーの描画領域の底部の座標を保存
                        Dim btm As Integer = rect.Bottom

                        'セルの描画領域のY座標
                        Select Case MyBase.BorderStyle
                            Case Windows.Forms.BorderStyle.None
                                rect.Y = rowHeight * j
                            Case Windows.Forms.BorderStyle.FixedSingle
                                rect.Y = rowHeight * j + lineWidth
                            Case Windows.Forms.BorderStyle.Fixed3D
                                rect.Y = rowHeight * j + (lineWidth * 2)
                        End Select

                        'セルの描画領域のX座標
                        rect.X -= lineWidth

                        'セルの描画領域の高さ
                        rect.Height = rowHeight

                        '最下行の場合高さを調整
                        If j = Me.ColumnHeaderRowCount - 1 Then
                            rect.Height = btm - rect.Y - lineWidth
                        End If

                        'セルを囲む線の描画
                        e.Graphics.DrawRectangle(gridPen, rect)


                        'セルの背景色の領域
                        rect.Y += lineWidth
                        rect.X += lineWidth
                        rect.Height -= lineWidth
                        rect.Width -= lineWidth

                        '背景色の描画
                        If ColumnHeaderBorderStyle <> HeaderCellBorderStyle.DoubleLine Then
                            'Single線の場合
                            e.Graphics.FillRectangle(backBrash, rect)
                        Else
                            'くぼみ線の場合
                            'rect.Width -= lineWidth
                            e.Graphics.FillRectangle(whiteBrash, rect)
                            rect.Y += lineWidth
                            rect.X += lineWidth
                            rect.Height -= lineWidth
                            rect.Width -= lineWidth

                            e.Graphics.FillRectangle(backBrash, rect)
                        End If

                        '見出しを最下列に表示
                        If j = Me.ColumnHeaderRowCount - 1 Then
                            Dim text As String = MyBase.Columns(i).HeaderText

                            If MyBase.SortedColumn IsNot Nothing AndAlso MyBase.SortedColumn Is Me.Columns(i) Then
                                If MyBase.SortOrder = Windows.Forms.SortOrder.Ascending Then
 text = text & "  ▼"
                                ElseIf MyBase.SortOrder = Windows.Forms.SortOrder.Descending Then
 text = text & "  ▲"
                                End If
                            End If

                            Dim formatFlg As TextFormatFlags = GetTextFormatFlags(MyBase.ColumnHeadersDefaultCellStyle.Alignment, _
MyBase.ColumnHeadersDefaultCellStyle.WrapMode)

                            TextRenderer.DrawText(e.Graphics, text, MyBase.ColumnHeadersDefaultCellStyle.Font, _
rect, MyBase.ColumnHeadersDefaultCellStyle.ForeColor, _
formatFlg)
                        End If

                    Finally
                        'リソースの解放
                        gridPen.Dispose()
                        backBrash.Dispose()
                        whiteBrash.Dispose()
                    End Try
                Next
            Next


            '---------------------------------------------------------------------------------------------------------
            'ヘッダーのセル結合
            '---------------------------------------------------------------------------------------------------------
            'ヘッダーセル定義の処理
            For i = 0 To Me.HeaderCells.Count - 1

                'セルの結合の開始行がヘッダーの行数より大きい場合は除外
                If HeaderCells(i).Row > Me.ColumnHeaderRowCount - 1 Then
                    Continue For
                End If

                'セルの結合の開始列の列インデックスが列数より大きい場合は除外
                If HeaderCells(i).Column > MyBase.ColumnCount - 1 Then
                    Continue For
                End If

                '描画領域の設定
                Dim rect As Rectangle = Nothing

                '結合する列中のソート状態
                Dim sortText As String = String.Empty

                '結合するセルの各列の幅を取得し描画領域の幅を決める、ソートされている列の場合Textに表示するソート方向の設定
                For j = Me.HeaderCells(i).Column To Me.HeaderCells(i).Column + Me.HeaderCells(i).ColumnSpan - 1

                    '列が画面に表示されていない場合は処理しない
                    If MyBase.Columns(j).Displayed = False Then
                        Continue For
                    End If

                    '列ヘッダーの領域の幅
                    If rect = Nothing Then
                        '結合するセルの開始列の場合
                        rect = MyBase.GetCellDisplayRectangle(j, -1, True)
                    Else
                        '結合するセルの2列目以降の場合
                        rect.Width += MyBase.GetCellDisplayRectangle(j, -1, True).Width
                    End If


                    'ソート列の場合
                    If HeaderCells(i).SortVisible = True AndAlso MyBase.SortedColumn IsNot Nothing AndAlso MyBase.SortedColumn Is MyBase.Columns(j) Then
                        If MyBase.SortOrder = Windows.Forms.SortOrder.Ascending Then
                            sortText = "  ▼"
                        ElseIf MyBase.SortOrder = Windows.Forms.SortOrder.Descending Then
                            sortText = "  ▲"
                        End If
                    End If

                Next

                '結合するセルが画面中に無い場合
                If rect = Nothing Then
                    Continue For
                End If

                '結合する行がヘッダー行数より大きい場合
                Dim rowSapn As Integer = Me.HeaderCells(i).RowSpan
                If rowSapn > ColumnHeaderRowCount Then
                    rowSapn = ColumnHeaderRowCount
                End If

                '列ヘッダーの描画領域の底部の座標を保存
                Dim btm As Integer = rect.Bottom

                '結合するセルの描画領域のY座標
                Select Case MyBase.BorderStyle
                    Case Windows.Forms.BorderStyle.None
                        rect.Y = rowHeight * (Me.HeaderCells(i).Row)
                    Case Windows.Forms.BorderStyle.FixedSingle
                        rect.Y = rowHeight * (Me.HeaderCells(i).Row) + lineWidth
                    Case Windows.Forms.BorderStyle.Fixed3D
                        rect.Y = rowHeight * (Me.HeaderCells(i).Row) + (lineWidth * 2)
                End Select

                '結合するセルの描画領域のX座標
                rect.X -= lineWidth

                '結合するセルの描画領域の高さ
                rect.Height = rowHeight * rowSapn

                '最下行の場合は描画領域の高さを調整する
                If Me.HeaderCells(i).Row + rowSapn = Me.ColumnHeaderRowCount Then
                    rect.Height = btm - rect.Y - lineWidth
                End If

                'グッリドの線
                Dim gridPen As New Pen(MyBase.GridColor)

                '背景色の取得
                Dim backgroundColor As System.Drawing.Color = MyBase.ColumnHeadersDefaultCellStyle.BackColor
                'セルの背景色が設定されている場合
                If Not Me.HeaderCells(i).BackgroundColor = Color.Empty Then
                    backgroundColor = Me.HeaderCells(i).BackgroundColor
                End If

                '背景色
                Dim backBrash As New SolidBrush(backgroundColor)

                'くぼみ線
                Dim whiteBrash As New SolidBrush(Color.White)

                Try

                    '枠線の描画
                    e.Graphics.DrawRectangle(gridPen, rect)


                    '結合セルの背景色の描画領域の設定
                    rect.Y += lineWidth
                    rect.X += lineWidth
                    rect.Height -= lineWidth
                    rect.Width -= lineWidth


                    '背景色の描画
                    If ColumnHeaderBorderStyle = HeaderCellBorderStyle.SingleLine Then
                        'Singleの場合
                        e.Graphics.FillRectangle(backBrash, rect)
                    Else
                        'くぼみ線の場合
                        e.Graphics.FillRectangle(whiteBrash, rect)
                        rect.Y += lineWidth
                        rect.X += lineWidth
                        rect.Height -= lineWidth
                        rect.Width -= lineWidth

                        e.Graphics.FillRectangle(backBrash, rect)
                    End If


                    'テキストの描画
                    Dim foreColor As System.Drawing.Color = MyBase.ColumnHeadersDefaultCellStyle.ForeColor
                    If Not Me.HeaderCells(i).ForeColor = Color.Empty Then
                        foreColor = Me.HeaderCells(i).ForeColor
                    End If

                    Dim formatFlg As TextFormatFlags = GetTextFormatFlags(Me.HeaderCells(i).TextAlign, Me.HeaderCells(i).WrapMode)

                    TextRenderer.DrawText(e.Graphics, Me.HeaderCells(i).Text & sortText, MyBase.ColumnHeadersDefaultCellStyle.Font, _
rect, foreColor, formatFlg)

                Finally
                    'リソースの解放
                    gridPen.Dispose()
                    backBrash.Dispose()
                    whiteBrash.Dispose()
                End Try
            Next

        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub

    ''' <summary>
    ''' 指定のスタイルから描写するテキストのスタイルを取得する
    ''' </summary>
    ''' <param name="alignment">テキストのスタイル</param>
    ''' <param name="wrapMode">折り返</param>
    ''' <remarks>描写するテキストのスタイル</remarks>
    Private Function GetTextFormatFlags(ByVal alignment As DataGridViewContentAlignment,
ByVal wrapMode As DataGridViewTriState) As TextFormatFlags
        Try
            ''文字の描画
            Dim formatFlg As TextFormatFlags = TextFormatFlags.Right Or TextFormatFlags.VerticalCenter Or TextFormatFlags.EndEllipsis

            '表示位置
            Select Case alignment
                Case DataGridViewContentAlignment.BottomCenter
                    formatFlg = TextFormatFlags.Bottom Or TextFormatFlags.HorizontalCenter Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.BottomLeft
                    formatFlg = TextFormatFlags.Bottom Or TextFormatFlags.Left Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.BottomRight
                    formatFlg = TextFormatFlags.Bottom Or TextFormatFlags.Right Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.MiddleCenter
                    formatFlg = TextFormatFlags.VerticalCenter Or TextFormatFlags.HorizontalCenter Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.MiddleLeft
                    formatFlg = TextFormatFlags.VerticalCenter Or TextFormatFlags.Left Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.MiddleRight
                    formatFlg = TextFormatFlags.VerticalCenter Or TextFormatFlags.Right Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.TopCenter
                    formatFlg = TextFormatFlags.Top Or TextFormatFlags.HorizontalCenter Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.TopLeft
                    formatFlg = TextFormatFlags.Top Or TextFormatFlags.Left Or TextFormatFlags.EndEllipsis
                Case DataGridViewContentAlignment.TopRight
                    formatFlg = TextFormatFlags.Top Or TextFormatFlags.Right Or TextFormatFlags.EndEllipsis
            End Select


            '折り返し
            Select Case wrapMode
                Case DataGridViewTriState.False
                Case DataGridViewTriState.NotSet
                Case DataGridViewTriState.True
                    formatFlg = formatFlg Or TextFormatFlags.WordBreak
            End Select

            Return formatFlg

        Catch ex As Exception
            Throw
        End Try
    End Function

    ''' <summary>
    ''' セルを結合する対象の列の描画領域の無効化
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub InvalidateUnitColumns()
        Try

            Dim hRect As Rectangle = MyBase.DisplayRectangle
            hRect.Height = MyBase.ColumnHeadersHeight + 1
            MyBase.Invalidate(hRect)

        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub

    ''' <summary>
    ''' スクロールが実行されたとき
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnScroll(ByVal e As System.Windows.Forms.ScrollEventArgs)
        MyBase.OnScroll(e)

        Try
            InvalidateUnitColumns()
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub

    ''' <summary>
    ''' サイズが変更されたとき
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
        MyBase.OnSizeChanged(e)

        Try
            InvalidateUnitColumns()
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try

    End Sub

    ''' <summary>
    ''' 列の幅が変更されたとき
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnColumnWidthChanged(ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs)
        MyBase.OnColumnWidthChanged(e)

        Try
            InvalidateUnitColumns()
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub

    ''' <summary>
    ''' 行の境界線がダブルクリックされた時
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnRowDividerDoubleClick(ByVal e As System.Windows.Forms.DataGridViewRowDividerDoubleClickEventArgs)
        MyBase.OnRowDividerDoubleClick(e)

        Try
            '行ヘッダーの境界線がダブルクリックされたへっだーの高さを整える
            If e.RowIndex = -1 Then
                MyBase.ColumnHeadersHeight = Me.ColumnHeaderRowCount * Me.ColumnHeaderRowHeight + 2
            End If
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub

    ''' <summary>
    ''' マウスのボタンが押された時
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseDown(e)

        Try
            '列幅、行高を調整するドラグ線を見えるようにするためにダブルバッファを解除する
            MyBase.DoubleBuffered = False
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub

    ''' <summary>
    ''' マウスのボタンが離された時
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnMouseUp(e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseUp(e)

        Try
            'OnMouseDownイベントで解除されたダブルバッファを適用する
            MyBase.DoubleBuffered = True
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub
End Class
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;


namespace ClassLibrary1
{
    class CustomHeaderDataGridView : System.Windows.Forms.DataGridView
    {
        private MyCollection _item = null;

        /// 
        /// 列ヘッダに表示するCellを設定します
        /// 
        /// 
        /// 
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("列ヘッダのカスタマイズ")]
        [Description("列ヘッダに表示するCellを設定します")]
        public MyCollection HeaderCells
        {
            get { return _item; }
        }

        internal void OnCollectionChanged()
        {
            this.Invalidate();
        }

        /// 
        /// コレクションの設定
        /// 
        /// 
        public class MyCollection : System.Collections.ObjectModel.Collection<HeaderCell>
        {

            private CustomHeaderDataGridView _parent;

            internal MyCollection(CustomHeaderDataGridView parent)
            {
                _parent = parent;
            }

            protected override void ClearItems()
            {
                base.ClearItems();
                _parent.OnCollectionChanged();
            }

            protected override void InsertItem(int index, HeaderCell item)
            {
                base.InsertItem(index, item);
                _parent.OnCollectionChanged();
            }

            protected override void RemoveItem(int index)
            {
                base.RemoveItem(index);
                _parent.OnCollectionChanged();
            }

            protected override void SetItem(int index, HeaderCell item)
            {
                base.SetItem(index, item);
                _parent.OnCollectionChanged();
            }

        }

        private int _columnHeaderRowCount = 1;
        /// 
        /// 列ヘッダーの行数を設定します
        /// 
        /// 
        /// 
        /// 
        [Category("列ヘッダのカスタマイズ")]
        [Description("列ヘッダに表示する行を設定します")]
        public int ColumnHeaderRowCount
        {
            get { return _columnHeaderRowCount; }
            set
            {
                _columnHeaderRowCount = value;

                if (value == 0)
                {
                    _columnHeaderRowCount = 1;
                }

                base.ColumnHeadersHeight = value * ColumnHeaderRowHeight + 2;
                base.Refresh();
            }
        }

        private int _columnHeaderRowHeight = 17;
        // 
        /// 列ヘッダに表示する行の高さ
        /// 
        /// 
        /// 
        /// 
        [Category("列ヘッダのカスタマイズ")]
        [Description("列ヘッダに表示する行の高さを設定します")]
        public int ColumnHeaderRowHeight
        {
            get { return _columnHeaderRowHeight; }
            set
            {
                _columnHeaderRowHeight = value;

                base.ColumnHeadersHeight = value * ColumnHeaderRowCount + 2;
                base.Refresh();
            }
        }

        /// 
        /// 列ヘッダーの境界線の種類
        /// 
        /// 
        public enum HeaderCellBorderStyle
        {
            SingleLine = 0,
            DoubleLine = 1
        }

        private HeaderCellBorderStyle _columnHeaderBorderStyle = HeaderCellBorderStyle.SingleLine;
        /// 
        /// 列ヘッダーの線種
        /// 
        /// 
        /// 
        /// 
        [Category("列ヘッダのカスタマイズ")]
        [Description("列ヘッダに線種を設定します")]
        public HeaderCellBorderStyle ColumnHeaderBorderStyle
        {
            get { return _columnHeaderBorderStyle; }
            set
            {
                _columnHeaderBorderStyle = value;
                base.Refresh();
            }
        }

        [System.Diagnostics.DebuggerNonUserCode()]
        public CustomHeaderDataGridView()
            : base()
        {
            //この呼び出しは、コンポーネント デザイナーで必要です。
            InitializeComponent();

            this._item = new MyCollection(this);
            base.DoubleBuffered = true;

        }


        //Component は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。
        [System.Diagnostics.DebuggerNonUserCode()]
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing && components != null)
                {
                    components.Dispose();
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        //コンポーネント デザイナーで必要です。
        private System.ComponentModel.IContainer components;

        //メモ: 以下のプロシージャはコンポーネント デザイナーで必要です。
        //コンポーネント デザイナーを使って変更できます。
        //コード エディターを使って変更しないでください。
        [System.Diagnostics.DebuggerStepThrough()]
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }





        /// 再描画をするとき
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e);
            try
            {


                //---------------------------------------------------------------------------------------------------------
                //ヘッダーセルの描画
                //---------------------------------------------------------------------------------------------------------

                //ヘッダーの行の高さの取得
                int rowHeight = base.ColumnHeadersHeight;

                if (this.ColumnHeaderRowCount > 0)
                {
                    rowHeight = base.ColumnHeadersHeight / this.ColumnHeaderRowCount;
                }

                //線の太さ
                int lineWidth = 1;

                for (int i = 0; i <= ColumnCount - 1; i++)
                {

                    for (int j = 0; j <= this.ColumnHeaderRowCount - 1; j++)
                    {

                        //グッリドの線
                        Pen gridPen = new Pen(base.GridColor);

                        //背景色
                        SolidBrush backBrash = new SolidBrush(base.ColumnHeadersDefaultCellStyle.BackColor);

                        //くぼみ線
                        SolidBrush whiteBrash = new SolidBrush(Color.White);

                        try
                        {
                            //列ヘッダーの描画領域
                            Rectangle rect = base.GetCellDisplayRectangle(i, -1, true);

                            //列ヘッダーの描画領域の底部の座標を保存
                            int btm = rect.Bottom;

                            //セルの描画領域のY座標
                            switch (base.BorderStyle)
                            {
                                case BorderStyle.None:
 rect.Y = rowHeight * j;
 break;
                                case BorderStyle.FixedSingle:
 rect.Y = rowHeight * j + lineWidth;
 break;
                                case BorderStyle.Fixed3D:
 rect.Y = rowHeight * j + (lineWidth * 2);
 break;
                            }

                            //セルの描画領域のX座標
                            rect.X -= lineWidth;

                            //セルの描画領域の高さ
                            rect.Height = rowHeight;

                            //最下行の場合高さを調整
                            if (j == this.ColumnHeaderRowCount - 1)
                            {
                                rect.Height = btm - rect.Y - lineWidth;
                            }

                            //セルを囲む線の描画
                            e.Graphics.DrawRectangle(gridPen, rect);


                            //セルの背景色の領域
                            rect.Y += lineWidth;
                            rect.X += lineWidth;
                            rect.Height -= lineWidth;
                            rect.Width -= lineWidth;

                            //背景色の描画
                            if (ColumnHeaderBorderStyle != HeaderCellBorderStyle.DoubleLine)
                            {
                                //Single線の場合
                                e.Graphics.FillRectangle(backBrash, rect);
                            }
                            else
                            {
                                //くぼみ線の場合
                                //rect.Width -= lineWidth
                                e.Graphics.FillRectangle(whiteBrash, rect);
                                rect.Y += lineWidth;
                                rect.X += lineWidth;
                                rect.Height -= lineWidth;
                                rect.Width -= lineWidth;

                                e.Graphics.FillRectangle(backBrash, rect);
                            }

                            //見出しを最下列に表示
                            if (j == this.ColumnHeaderRowCount - 1)
                            {
                                string text = base.Columns[i].HeaderText;

                                if (base.SortedColumn != null && object.ReferenceEquals(base.SortedColumn, this.Columns[i]))
                                {
 if (base.SortOrder == SortOrder.Ascending)
 {
text = text + " ▼";
 }
 else if (base.SortOrder == SortOrder.Descending)
 {
text = text + " ▲";
 }
                                }

                                TextFormatFlags formatFlg = GetTextFormatFlags(base.ColumnHeadersDefaultCellStyle.Alignment, base.ColumnHeadersDefaultCellStyle.WrapMode);

                                TextRenderer.DrawText(e.Graphics, text, base.ColumnHeadersDefaultCellStyle.Font, rect, base.ColumnHeadersDefaultCellStyle.ForeColor, formatFlg);
                            }

                        }
                        finally
                        {
                            //リソースの解放
                            gridPen.Dispose();
                            backBrash.Dispose();
                            whiteBrash.Dispose();
                        }
                    }
                }

                //---------------------------------------------------------------------------------------------------------
                //ヘッダーのセル結合
                //---------------------------------------------------------------------------------------------------------
                //ヘッダーセル定義の処理
                for (int i = 0; i <= this.HeaderCells.Count - 1; i++)
                {

                    //セルの結合の開始行がヘッダーの行数より大きい場合は除外
                    if (HeaderCells[i].Row > this.ColumnHeaderRowCount - 1)
                    {
                        continue;
                    }

                    //セルの結合の開始列の列インデックスが列数より大きい場合は除外
                    if (HeaderCells[i].Column > base.ColumnCount - 1)
                    {
                        continue;
                    }

                    //描画領域の設定
                    Rectangle rect = Rectangle.Empty;

                    //結合する列中のソート状態
                    string sortText = string.Empty;

                    //結合するセルの各列の幅を取得し描画領域の幅を決める、ソートされている列の場合Textに表示するソート方向の設定
                    for (int j = this.HeaderCells[i].Column; j <= this.HeaderCells[i].Column + this.HeaderCells[i].ColumnSpan - 1; j++)
                    {

                        //列が画面に表示されていない場合は処理しない
                        if (base.Columns[j].Displayed == false)
                        {
                            continue;
                        }

                        //列ヘッダーの領域の幅
                        if (rect.IsEmpty)
                        {
                            //結合するセルの開始列の場合
                            rect = base.GetCellDisplayRectangle(j, -1, true);
                        }
                        else
                        {
                            //結合するセルの2列目以降の場合
                            rect.Width += base.GetCellDisplayRectangle(j, -1, true).Width;
                        }


                        //ソート列の場合
                        if (HeaderCells[i].SortVisible == true && base.SortedColumn != null && object.ReferenceEquals(base.SortedColumn, base.Columns[j]))
                        {
                            if (base.SortOrder == SortOrder.Ascending)
                            {
                                sortText = " ▼";
                            }
                            else if (base.SortOrder == SortOrder.Descending)
                            {
                                sortText = " ▲";
                            }
                        }

                    }

                    //結合するセルが画面中に無い場合
                    if (rect == null)
                    {
                        continue;
                    }

                    //結合する行がヘッダー行数より大きい場合
                    int rowSapn = this.HeaderCells[i].RowSpan;
                    if (rowSapn > ColumnHeaderRowCount)
                    {
                        rowSapn = ColumnHeaderRowCount;
                    }

                    //列ヘッダーの描画領域の底部の座標を保存
                    int btm = rect.Bottom;

                    //結合するセルの描画領域のY座標
                    switch (base.BorderStyle)
                    {
                        case BorderStyle.None:
                            rect.Y = rowHeight * (this.HeaderCells[i].Row);
                            break;
                        case BorderStyle.FixedSingle:
                            rect.Y = rowHeight * (this.HeaderCells[i].Row) + lineWidth;
                            break;
                        case BorderStyle.Fixed3D:
                            rect.Y = rowHeight * (this.HeaderCells[i].Row) + (lineWidth * 2);
                            break;
                    }

                    //結合するセルの描画領域のX座標
                    rect.X -= lineWidth;

                    //結合するセルの描画領域の高さ
                    rect.Height = rowHeight * rowSapn;

                    //最下行の場合は描画領域の高さを調整する
                    if (this.HeaderCells[i].Row + rowSapn == this.ColumnHeaderRowCount)
                    {
                        rect.Height = btm - rect.Y - lineWidth;
                    }

                    //グッリドの線
                    Pen gridPen = new Pen(base.GridColor);

                    //背景色の取得
                    System.Drawing.Color backgroundColor = base.ColumnHeadersDefaultCellStyle.BackColor;
                    //セルの背景色が設定されている場合
                    if (!(this.HeaderCells[i].BackgroundColor == Color.Empty))
                    {
                        backgroundColor = this.HeaderCells[i].BackgroundColor;
                    }

                    //背景色
                    SolidBrush backBrash = new SolidBrush(backgroundColor);

                    //くぼみ線
                    SolidBrush whiteBrash = new SolidBrush(Color.White);

                    try
                    {

                        //枠線の描画
                        e.Graphics.DrawRectangle(gridPen, rect);


                        //結合セルの背景色の描画領域の設定
                        rect.Y += lineWidth;
                        rect.X += lineWidth;
                        rect.Height -= lineWidth;
                        rect.Width -= lineWidth;


                        //背景色の描画
                        if (ColumnHeaderBorderStyle == HeaderCellBorderStyle.SingleLine)
                        {
                            //Singleの場合
                            e.Graphics.FillRectangle(backBrash, rect);
                        }
                        else
                        {
                            //くぼみ線の場合
                            e.Graphics.FillRectangle(whiteBrash, rect);
                            rect.Y += lineWidth;
                            rect.X += lineWidth;
                            rect.Height -= lineWidth;
                            rect.Width -= lineWidth;

                            e.Graphics.FillRectangle(backBrash, rect);
                        }


                        //テキストの描画
                        System.Drawing.Color foreColor = base.ColumnHeadersDefaultCellStyle.ForeColor;
                        if (!(this.HeaderCells[i].ForeColor == Color.Empty))
                        {
                            foreColor = this.HeaderCells[i].ForeColor;
                        }

                        TextFormatFlags formatFlg = GetTextFormatFlags(this.HeaderCells[i].TextAlign, this.HeaderCells[i].WrapMode);

                        TextRenderer.DrawText(e.Graphics, this.HeaderCells[i].Text + sortText, base.ColumnHeadersDefaultCellStyle.Font, rect, foreColor, formatFlg);

                    }
                    finally
                    {
                        //リソースの解放
                        gridPen.Dispose();
                        backBrash.Dispose();
                        whiteBrash.Dispose();
                    }
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }


        /// <summary>
        /// 指定のスタイルから描写するテキストのスタイルを取得する
        /// </summary>
        /// <param name="alignment">テキストのスタイル</param>
        /// <param name="wrapMode">折り返</param>
        /// <remarks>描写するテキストのスタイル</remarks>
        private TextFormatFlags GetTextFormatFlags(DataGridViewContentAlignment alignment, DataGridViewTriState wrapMode)
        {
            try
            {
                //'文字の描画
                TextFormatFlags formatFlg = TextFormatFlags.Right | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis;

                //表示位置
                switch (alignment)
                {
                    case DataGridViewContentAlignment.BottomCenter:
                        formatFlg = TextFormatFlags.Bottom | TextFormatFlags.HorizontalCenter | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.BottomLeft:
                        formatFlg = TextFormatFlags.Bottom | TextFormatFlags.Left | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.BottomRight:
                        formatFlg = TextFormatFlags.Bottom | TextFormatFlags.Right | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.MiddleCenter:
                        formatFlg = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.MiddleLeft:
                        formatFlg = TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.MiddleRight:
                        formatFlg = TextFormatFlags.VerticalCenter | TextFormatFlags.Right | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.TopCenter:
                        formatFlg = TextFormatFlags.Top | TextFormatFlags.HorizontalCenter | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.TopLeft:
                        formatFlg = TextFormatFlags.Top | TextFormatFlags.Left | TextFormatFlags.EndEllipsis;
                        break;
                    case DataGridViewContentAlignment.TopRight:
                        formatFlg = TextFormatFlags.Top | TextFormatFlags.Right | TextFormatFlags.EndEllipsis;
                        break;
                }


                //折り返し
                switch (wrapMode)
                {
                    case DataGridViewTriState.False:
                        break;
                    case DataGridViewTriState.NotSet:
                        break;
                    case DataGridViewTriState.True:
                        formatFlg = formatFlg | TextFormatFlags.WordBreak;
                        break;
                }

                return formatFlg;

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// セルを結合する対象の列の描画領域の無効化
        /// </summary>
        /// <remarks></remarks>
        private void InvalidateUnitColumns()
        {
            try
            {

                Rectangle hRect = base.DisplayRectangle;
                hRect.Height = base.ColumnHeadersHeight + 1;
                base.Invalidate(hRect);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// スクロールが実行されたとき
        /// </summary>
        /// <param name="e"></param>
        /// <remarks></remarks>
        protected override void OnScroll(System.Windows.Forms.ScrollEventArgs e)
        {
            base.OnScroll(e);

            try
            {
                InvalidateUnitColumns();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// サイズが変更されたとき
        /// </summary>
        /// <param name="e"></param>
        /// <remarks></remarks>
        protected override void OnSizeChanged(System.EventArgs e)
        {
            base.OnSizeChanged(e);

            try
            {
                InvalidateUnitColumns();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

        }

        /// <summary>
        /// 列の幅が変更されたとき
        /// </summary>
        /// <param name="e"></param>
        /// <remarks></remarks>
        protected override void OnColumnWidthChanged(System.Windows.Forms.DataGridViewColumnEventArgs e)
        {
            base.OnColumnWidthChanged(e);

            try
            {
                InvalidateUnitColumns();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// 行の境界線がダブルクリックされた時
        /// </summary>
        /// <param name="e"></param>
        /// <remarks></remarks>
        protected override void OnRowDividerDoubleClick(System.Windows.Forms.DataGridViewRowDividerDoubleClickEventArgs e)
        {
            base.OnRowDividerDoubleClick(e);

            try
            {
                //行ヘッダーの境界線がダブルクリックされたへっだーの高さを整える
                if (e.RowIndex == -1)
                {
                    base.ColumnHeadersHeight = this.ColumnHeaderRowCount * this.ColumnHeaderRowHeight + 2;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// マウスのボタンが押された時
        /// </summary>
        /// <param name="e"></param>
        /// <remarks></remarks>
        protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseDown(e);

            try
            {
                //列幅、行高を調整するドラグ線を見えるようにするためにダブルバッファを解除する
                base.DoubleBuffered = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// マウスのボタンが離された時
        /// </summary>
        /// <param name="e"></param>
        /// <remarks></remarks>
        protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseUp(e);

            try
            {
                //OnMouseDownイベントで解除されたダブルバッファを適用する
                base.DoubleBuffered = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
    }
}