DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

DataGridViewのセルを自分で描画する

注意:DataGridViewコントロールは、.NET Framework 2.0で新しく追加されました。

DataGridViewのセルを自分で描画するには、DataGridView.CellPaintingイベントを使用する方法や、カスタムセルクラスを作成してPaintメソッドをオーバーライドする方法などがあります。ここでは、CellPaintingイベントを使用する方法を紹介します。カスタムセルクラスを作成する方法は、こちらで紹介しています。

補足:行の描画は、RowPrePaint、RowPostPaintイベントで行うこともできます。行の描画を行う方法は、こちらで紹介しています。

早速ですが、CellPaintingイベントを使用した幾つかの例を紹介します。

セルの背景にグラデーションをかける

まずは、CellPaintingイベントを使用して、セルの背景にグラデーションがかかるようにしてみましょう。

背景にグラデーションのかかったセル

VB.NET
コードを隠すコードを選択
'CellPaintingイベントハンドラ
Private Sub DataGridView1_CellPainting(ByVal sender As Object, _
        ByVal e As DataGridViewCellPaintingEventArgs) _
        Handles DataGridView1.CellPainting
    'ヘッダー以外のセルで、背景を描画する時
    If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 AndAlso _
        (e.PaintParts And DataGridViewPaintParts.Background) = _
            DataGridViewPaintParts.Background Then

        '選択されているか調べ、色を決定する
        'bColor1が開始色、bColor2が終了色
        Dim bColor1, bColor2 As Color
        If (e.PaintParts And DataGridViewPaintParts.SelectionBackground) = _
                DataGridViewPaintParts.SelectionBackground AndAlso _
            (e.State And DataGridViewElementStates.Selected) = _
                DataGridViewElementStates.Selected Then
            bColor1 = e.CellStyle.SelectionBackColor
            bColor2 = Color.Black
        Else
            bColor1 = e.CellStyle.BackColor
            bColor2 = Color.LemonChiffon
        End If

        'グラデーションブラシを作成
        Dim b As New System.Drawing.Drawing2D.LinearGradientBrush( _
            e.CellBounds, bColor1, bColor2, _
            System.Drawing.Drawing2D.LinearGradientMode.Horizontal)
        Try
            'セルを塗りつぶす
            e.Graphics.FillRectangle(b, e.CellBounds)
        Finally
            b.Dispose()
        End Try

        '背景以外が描画されるようにする
        Dim paintParts As DataGridViewPaintParts = _
            e.PaintParts And Not DataGridViewPaintParts.Background
        'セルを描画する
        e.Paint(e.ClipBounds, paintParts)

        '描画が完了したことを知らせる
        e.Handled = True
    End If
End Sub
C#
コードを隠すコードを選択
//CellPaintingイベントハンドラ
private void DataGridView1_CellPainting(object sender,
    DataGridViewCellPaintingEventArgs e)
{
    //ヘッダー以外のセルで、背景を描画する時
    if (e.ColumnIndex >= 0 && e.RowIndex >= 0 &&
        (e.PaintParts & DataGridViewPaintParts.Background) ==
            DataGridViewPaintParts.Background)
    {
        //選択されているか調べ、色を決定する
        //bColor1が開始色、bColor2が終了色
        Color bColor1, bColor2;
        if ((e.PaintParts & DataGridViewPaintParts.SelectionBackground) ==
                DataGridViewPaintParts.SelectionBackground &&
            (e.State & DataGridViewElementStates.Selected) ==
                DataGridViewElementStates.Selected)
        {
            bColor1 = e.CellStyle.SelectionBackColor;
            bColor2 = Color.Black;
        }
        else
        {
            bColor1 = e.CellStyle.BackColor;
            bColor2 = Color.LemonChiffon;
        }
        //グラデーションブラシを作成
        using (System.Drawing.Drawing2D.LinearGradientBrush b =
            new System.Drawing.Drawing2D.LinearGradientBrush(
            e.CellBounds, bColor1, bColor2,
            System.Drawing.Drawing2D.LinearGradientMode.Horizontal))
        {
            //セルを塗りつぶす
            e.Graphics.FillRectangle(b, e.CellBounds);
        }

        //背景以外が描画されるようにする
        DataGridViewPaintParts paintParts =
           e.PaintParts & ~DataGridViewPaintParts.Background;
        //セルを描画する
        e.Paint(e.ClipBounds, paintParts);

        //描画が完了したことを知らせる
        e.Handled = true;
    }
}

ここでは、まずLinearGradientBrushでセルを塗りつぶし、その後セルの背景以外の部分を描画しています。

なお、CellPaintingイベントハンドラでは、描画するセルが列ヘッダーの場合は「e.RowIndex」が-1に、行ヘッダーの場合は「e.ColumnIndex」が-1になります。

補足:ヘッダーセルの背景を上記と同じ方法で描画すると、セルの境界線が消えてしまいます。

DataGridViewCellPaintingEventArgs.Paintメソッドについて

DataGridViewCellPaintingEventArgs.Paintメソッドを使えば、セルの指定した部分(セルの境界線、前景、背景など)だけを描画することができます。これを使えば、上の例のように、セルの背景は自分で描画して、セルの前景(テキストなどのコンテンツ部分)は通常通りに描画するということができます。あるいはその逆に、コンテンツ部分を自分で描画して、その他は普通通りに描画することもできます。

描画する部分の指定は、Paintメソッドの2番目のパラメータにDataGridViewPaintParts列挙値(ビットの組み合わせ)を渡すことにより行います。DataGridViewPaintParts列挙体の値とその意味は、以下の通りです(MSDN「DataGridViewPaintParts 列挙体」からの抜粋)。

DataGridViewPaintParts列挙体のメンバ名 説明
All セルのすべての部分を描画する必要があります。
Background セルの背景を描画する必要があります。
Border セルの境界線を描画する必要があります。
ContentBackground セルの内容の背景を描画する必要があります。
ContentForeground セルの内容の前景を描画する必要があります。
ErrorIcon セルのエラー アイコンを描画する必要があります。
Focus セルの周囲にフォーカスを示す四角形を描画する必要があります。
None 何も描画しません。
SelectionBackground セルが選択されたときに、セルの背景を描画する必要があります。

上記のコードでは、DataGridViewCellPaintingEventArgs.Paintメソッドでセルの描画する部分を指定する時、e.PaintParts(本来描画されるべき部分)からBackgroundを除外して、背景が描画されないようにしています。

補足:ちなみに、SelectionBackgroundを除外した場合は、セルが選択されていても、選択されていない時と同じ背景色で描画されます。

このようにDataGridViewCellPaintingEventArgs.Paintメソッドを使う以外に、PaintBackgroundやPaintContentを使用してセルの一部を描画することもできます。

DataGridViewCellPaintingEventArgs.PaintBackgroundメソッドでは、セルの境界線と背景が描画されます。具体的には、DataGridViewPaintPartsのBorderとBackgroundを指定してPaintメソッドを呼び出したのと同じになります。さらにPaintBackgroundの第2パラメータにTrueを指定すると、SelectionBackgroundも指定されます。

DataGridViewCellPaintingEventArgs.PaintContentメソッドでは、セルの内容を描画します。具体的には、DataGridViewPaintPartsのErrorIcon、ContentForeground、ContentBackgroundを指定してPaintメソッドを呼び出したのと同じになります。

補足:よって、PaintBackgroundとPaintContentを呼び出してもフォーカス枠(DataGridViewPaintParts.Focus)は描画されません。

セルの背景に画像を表示する

セルの背景に画像を表示する方法も、先の例を理解できれば、簡単です。

VB.NET
コードを隠すコードを選択
'セルの背景に表示する画像
Private cellBackImage As New Bitmap("C:\back.gif")

'CellPaintingイベントハンドラ
Private Sub DataGridView1_CellPainting(ByVal sender As Object, _
        ByVal e As DataGridViewCellPaintingEventArgs) _
        Handles DataGridView1.CellPainting
    'ヘッダー以外のセルで、背景を描画する時
    If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 AndAlso _
        (e.PaintParts And DataGridViewPaintParts.Background) = _
            DataGridViewPaintParts.Background Then
        '背景だけを描画する
        Dim backParts As DataGridViewPaintParts = _
            e.PaintParts And (DataGridViewPaintParts.Background Or _
                DataGridViewPaintParts.SelectionBackground)
        e.Paint(e.ClipBounds, backParts)

        '画像をセルの範囲いっぱいに表示する
        e.Graphics.DrawImage(cellBackImage, e.CellBounds)

        '背景以外が描画されるようにする
        Dim paintParts As DataGridViewPaintParts = _
            e.PaintParts And Not backParts
        'セルを描画する
        e.Paint(e.ClipBounds, paintParts)

        '描画が完了したことを知らせる
        e.Handled = True
    End If
End Sub
C#
コードを隠すコードを選択
//セルの背景に表示する画像
private Bitmap cellBackImage = new Bitmap("C:\\back.gif");

//CellPaintingイベントハンドラ
private void DataGridView1_CellPainting(object sender,
    DataGridViewCellPaintingEventArgs e)
{
    //ヘッダー以外のセルで、背景を描画する時
    if (e.ColumnIndex >= 0 && e.RowIndex >= 0 &&
        (e.PaintParts & DataGridViewPaintParts.Background) ==
            DataGridViewPaintParts.Background)
    {
        //背景だけを描画する
        DataGridViewPaintParts backParts = e.PaintParts &
            (DataGridViewPaintParts.Background |
            DataGridViewPaintParts.SelectionBackground);
        e.Paint(e.ClipBounds, backParts);

        //画像をセルの範囲いっぱいに表示する
        e.Graphics.DrawImage(cellBackImage, e.CellBounds);

        //背景以外が描画されるようにする
        DataGridViewPaintParts paintParts =
            e.PaintParts & ~backParts;
        //セルを描画する
        e.Paint(e.ClipBounds, paintParts);

        //描画が完了したことを知らせる
        e.Handled = true;
    }
}

ここでは先の例と違って、はじめにセルの背景だけを描画しています。

なおセルの背景に画像を表示する方法としては、「画像で塗りつぶす」で紹介したようなTextureBrushを使用して、背景を画像で塗りつぶす方法も考えられます。

ヘッダーセルを描画する

先述したとおり、CellPaintingではヘッダーの描画もできます。CellPaintingイベントハンドラでは、描画するセルが列ヘッダーの場合はDataGridViewCellPaintingEventArgsオブジェクトのRowIndexプロパティが-1になり、行ヘッダーの場合はColumnIndexプロパティが-1になります。その他は、通常のセルを描画する方法とほぼ同じです。

なお、CellPaintingイベントを使用して行ヘッダーに行番号を表示する方法を、こちらで紹介しています。

  • 履歴:
  • 2010/6/29 「セルの背景に画像を表示する」で、セルの範囲よりも大きく画像を描画するとセルの外にも描画されてしまうことがあるため、セル内にだけ描画するようにした。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。