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

DataGridのセルに画像を表示する

注意:ここで紹介しているDataGridは、System.Windows.Forms名前空間のDataGrid(Windowsフォーム)です。System.Web.UI.WebControls名前空間のDataGrid(Webフォーム)ではありません。

DataGridColumnStyle.PaintメソッドをオーバーライドしたDataGridColumnStyleからの派生クラスを作成することにより、セルに画像を表示できるようになります。

まず次のようなクラスを作成し、このPaintメソッド内で画像を描画するようにします。下の例ではDataGridTextBoxColumnの派生クラスDataGridImageTextBoxColumnクラスを作成し、セルの値が画像ファイル名と解釈してこの描画を試みます。

VB.NET
コードを隠すコードを選択
'セルに画像を表示するためのクラス
'セルの値(文字列)をファイル名としてその画像を表示する
'DataGridTextBoxColumnを継承してクラスを作成
Public Class DataGridImageTextBoxColumn
    Inherits DataGridTextBoxColumn

    'Paintメソッドをオーバーライドする
    Protected Overloads Overrides Sub Paint( _
            ByVal g As Graphics, _
            ByVal bounds As Rectangle, _
            ByVal source As CurrencyManager, _
            ByVal rowNum As Integer, _
            ByVal backBrush As Brush, _
            ByVal foreBrush As Brush, _
            ByVal alignToRight As Boolean _
            )
        'セルの値を取得する
        Dim cellValue As Object = _
            Me.GetColumnValueAtRow(source, rowNum)
        If (Not cellValue Is Nothing) Then
            '表示するファイル名の取得
            Dim fileName As String
            fileName = CType(cellValue, String)
            Try
                '画像を読み込む
                Dim bmp As New Bitmap(fileName)
                'boundsの大きさで画像を表示する
                '(セルの大きさに合わせて画像が表示される)
                g.DrawImage(bmp, bounds)
            Catch ex As Exception
                '画像を読み込めなかったときは普通に表示
                MyBase.Paint(g, bounds, source, rowNum, _
                    backBrush, foreBrush, alignToRight)
            End Try
        End If
    End Sub

    '編集できないようにEditメソッドをオーバーライドする
    Protected Overloads Overrides Sub Edit( _
        ByVal source As CurrencyManager, _
        ByVal rowNum As Integer, _
        ByVal bounds As Rectangle, _
        ByVal [readOnly] As Boolean, _
        ByVal instantText As String, _
        ByVal cellIsVisible As Boolean)
    End Sub
End Class
C#
コードを隠すコードを選択
//セルに画像を表示するためのクラス
//セルの値(文字列)をファイル名としてその画像を表示する
//DataGridTextBoxColumnを継承してクラスを作成
public class DataGridImageTextBoxColumn : DataGridTextBoxColumn
{
    //Paintメソッドをオーバーライドする
    protected  override void Paint(Graphics g, 
                                Rectangle bounds, 
                                CurrencyManager source, 
                                int rowNum, 
                                Brush backBrush, 
                                Brush foreBrush, 
                                bool alignToRight)
    {
        //セルの値を取得する
        object cellValue =
            this.GetColumnValueAtRow(source, rowNum);
        if (cellValue != null)
        {
            //表示するファイル名の取得
            string fileName;
            fileName = (string) cellValue;
            try
            {
                //画像を読み込む
                Bitmap bmp = new Bitmap(fileName);
                //boundsの大きさで画像を表示する
                //(セルの大きさに合わせて画像が表示される)
                g.DrawImage(bmp, bounds);
            }
            catch (Exception ex)
            {
                //画像を読み込めなかったときは普通に表示
                base.Paint(g, bounds, source, rowNum,
                    backBrush, foreBrush, alignToRight);
            }
        }
    }

    //編集できないようにEditメソッドをオーバーライドする
    protected  override void Edit(CurrencyManager source, 
                                int rowNum, 
                                Rectangle bounds,
                                bool readOnly,
                                string instantText,
                                bool cellIsVisible)
    {
    }
}

次に画像を表示したい列にこの列スタイルを使うようにします。テーブルスタイルの説明は省かれていますので、分からないという方はこちらをご覧ください。以下の例では、データソースが"Column1"という名前のDataColumnがある"DataTable1"という名前のDataTableであることを前提にしています。

VB.NET
コードを隠すコードを選択
'新しいDataGridTableStyleの作成
Dim ts As New DataGridTableStyle()
'マップ名を指定する
ts.MappingName = "DataTable1"

'適当な行の高さを指定する
ts.PreferredRowHeight = 200

'列スタイルにDataGridImageTextBoxColumnを使う
Dim cs As DataGridImageTextBoxColumn
cs = New DataGridImageTextBoxColumn()
'マップ名を指定する
cs.MappingName = "Column1"
'適当な幅を指定する
cs.Width = 200
'DataGridTableStyleに追加する
ts.GridColumnStyles.Add(cs)

'テーブルスタイルをDataGridに追加する
DataGrid1.TableStyles.Add(ts)
C#
コードを隠すコードを選択
//新しいDataGridTableStyleの作成
DataGridTableStyle ts = new DataGridTableStyle();
//マップ名を指定する
ts.MappingName = "DataTable1";

//適当な行の高さを指定する
ts.PreferredRowHeight = 200;

//列スタイルにDataGridImageTextBoxColumnを使う
DataGridImageTextBoxColumn cs;
cs = new DataGridImageTextBoxColumn();
//マップ名を指定する
cs.MappingName = "Column1";
//適当な幅を指定する
cs.Width = 200;
//DataGridTableStyleに追加する
ts.GridColumnStyles.Add(cs);

//テーブルスタイルをDataGridに追加する
DataGrid1.TableStyles.Add(ts);
補足:上記のDataGridImageTextBoxColumnクラスはあくまでDataGridのセルに画像を表示する方法を紹介する目的で作成されたため、Paintメソッドで毎回画像を読み込むという、非効率な処理を承知でやっています。このことは書かずとも分かっていただけるだろうと思っていたのですが、そうでもなかったようです。

そこで、毎回画像を読み込むのではなく、読み込んでいない画像のみを読み込むように改良したクラスを以下に紹介します。しかしこれもまだ欠陥が山ほどあることをご了承ください。(画像の縮小率が決まっている時は縮小しておく、画像データが溜まり過ぎないようにするなどの改良が考えられます。)
VB.NET
コードを隠すコードを選択
'セルに画像を表示するためのクラス
'セルの値(文字列)をファイル名としてその画像を表示する
'DataGridTextBoxColumnを継承してクラスを作成
Public Class DataGridImageTextBoxColumn
    Inherits DataGridTextBoxColumn
    '画像データを覚えておく
    Private _images As New Hashtable

    'Paintメソッドをオーバーライドする
    Protected Overloads Overrides Sub Paint(ByVal g As Graphics, _
            ByVal bounds As Rectangle, _
            ByVal [source] As CurrencyManager, _
            ByVal rowNum As Integer, _
            ByVal backBrush As Brush, _
            ByVal foreBrush As Brush, _
            ByVal alignToRight As Boolean)
        'セルの値を取得する
        Dim cellValue As Object = _
            Me.GetColumnValueAtRow([source], rowNum)

        If Not (cellValue Is Nothing) AndAlso _
            Not (cellValue Is DBNull.Value) Then
            '表示するファイル名の取得
            Dim fileName As String
            fileName = CStr(cellValue)
            '画像ファイルが読み込まれていない時だけ読み込む
            If Not _images.Contains(fileName) Then
                Try
                    '画像を読み込む
                    _images.Add(fileName, New Bitmap(fileName))
                Catch
                End Try
            End If
            '画像を読み込めなかった時
            Dim bmp As Bitmap = CType(_images(fileName), Bitmap)
            If Not (bmp Is Nothing) Then
                'boundsの大きさで画像を表示する
                '(セルの大きさに合わせて画像が表示される)
                g.DrawImage(bmp, bounds)
            Else
                '画像を読み込めなかったときは普通に表示
                MyBase.Paint(g, bounds, [source], rowNum, backBrush, _
                    foreBrush, alignToRight)
            End If
        End If
    End Sub 'Paint

    '編集できないようにEditメソッドをオーバーライドする
    Protected Overloads Overrides Sub Edit( _
        ByVal [source] As CurrencyManager, _
        ByVal rowNum As Integer, _
        ByVal bounds As Rectangle, _
        ByVal [readOnly] As Boolean, _
        ByVal instantText As String, _
        ByVal cellIsVisible As Boolean)
    End Sub
End Class
C#
コードを隠すコードを選択
//セルに画像を表示するためのクラス
//セルの値(文字列)をファイル名としてその画像を表示する
//DataGridTextBoxColumnを継承してクラスを作成
public class DataGridImageTextBoxColumn : DataGridTextBoxColumn
{
    //画像データを覚えておく
    Hashtable _images = new Hashtable();

    //Paintメソッドをオーバーライドする
    protected  override void Paint(Graphics g, 
        Rectangle bounds, 
        CurrencyManager source, 
        int rowNum, 
        Brush backBrush, 
        Brush foreBrush, 
        bool alignToRight)
    {
        //セルの値を取得する
        object cellValue =
            this.GetColumnValueAtRow(source, rowNum);
        if (cellValue != null && cellValue != DBNull.Value)
        {
            //表示するファイル名の取得
            string fileName;
            fileName = (string) cellValue;
            //画像ファイルが読み込まれていない時だけ読み込む
            if (!_images.Contains(fileName))
            {
                try
                {
                    //画像を読み込む
                    _images.Add(fileName, new Bitmap(fileName));
                }
                catch
                {
                    //画像を読み込めなかった時
                    _images.Add(fileName, null);
                }
            }
            Bitmap bmp = (Bitmap) _images[fileName];
            if (bmp != null)
            {
                //boundsの大きさで画像を表示する
                //(セルの大きさに合わせて画像が表示される)
                g.DrawImage(bmp, bounds);
            }
            else
            {
                //画像を読み込めなかったときは普通に表示
                base.Paint(g, bounds, source, rowNum,
                    backBrush, foreBrush, alignToRight);
            }
        }
    }

    //編集できないようにEditメソッドをオーバーライドする
    protected override void Edit(CurrencyManager source, 
        int rowNum, 
        Rectangle bounds,
        bool readOnly,
        string instantText,
        bool cellIsVisible)
    {
    }
}

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

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。