.NET TIPS

ListBoxコントロールのオーナー描画(高さ固定)により画像を一覧表示するには?

デジタルアドバンテージ 遠藤 孝信
2005/08/19

 Windowsアプリケーションのリストボックス(ListBoxコントロール)では、その項目として文字列を利用するのが一般的であるが、「オーナー描画」(オーナードロー)と呼ばれる機能により、独自に項目を描画することもできる。

 本稿では、このオーナー描画を利用して、以下のようにリストボックスに画像を一覧表示する方法について解説する。

オーナー描画によりリストボックスに画像を表示するサンプル・アプリケーションの実行画面
ボタンをクリックすることにより、特定のディレクトリに格納されているJPEG画像を一覧表示する。各項目の高さは一定であり、ここでは画像全体が1つの項目内に収まるように縮小(あるいは拡大)して表示している。画面は上から2番目の項目を選択しているところ。

 なお、ListBoxコントロールのオーナー描画では、表示する項目の高さを固定(一定)にするか、可変(項目によって高さが異なる)にするかを選択することができるが、本稿では高さが固定の場合についてのみ解説する。高さを可変にする方法については「TIPS:ListBoxコントロールのオーナー描画(高さ可変)により画像を一覧表示するには? 」で解説している。

ListBoxコントロールのプロパティ設定

 ListBoxコントロールでオーナー描画(高さ固定)を利用するには、まずDrawModeプロパティにDrawMode.OwnerDrawFixed(System.Windows.Forms名前空間のDrawMode列挙体の値)を設定する。これにより、各項目を描画する必要が生じた場合にDrawItemイベントが発生するようになる。

 項目の高さはItemHeightプロパティにより指定できる(最大255まで)。

項目の追加

 次にListBoxコントロールのItemsプロパティに対してAddメソッドにより項目を追加していく。文字列のみのリストボックスの場合は、Addメソッドのパラメータに、項目として表示したい文字列を渡すが、オーナー描画の場合には任意のオブジェクトでよい(詳細は後述)。

 後掲するサンプル・プログラムでは、以下のように項目として描画する画像(Imageオブジェクト)をAddメソッドにより順に追加している。

listBox1.Items.Add(<Imageオブジェクト>)

 このようにした場合、項目を追加した順番にインデックス番号が割り振られるので、そのインデックス番号を使って任意のImageオブジェクトを次のようにして取り出すことができる(C#の場合。VB.NETの場合はサンプル・プログラムを参照)。

Image thumbnail = (Image)listBox1.Items[<インデックス番号>];

 あるいは、表示する画像のImageオブジェクトを配列などで保持しておき、そのインデックス番号をAddメソッドによりItemsプロパティに追加してもよい。

項目の描画時に発生するDrawItemイベント

 上述したように、項目の描画が必要になった場合には、項目ごとにDrawItemイベントが発生する。このイベントのイベント・ハンドラでは、メソッドのパラメータとして渡されるDrawItemEventArgsクラス(System.Windows.Forms名前空間)のオブジェクトのプロパティから、以下のようなオブジェクトにアクセスできる。

プロパティ名 概要
Index 描画すべき項目のインデックス番号
Bounds 項目を描画する領域を示す四角形(Rectangleオブジェクト)
Graphics 描画対象となるGraphicsオブジェクト。例えばGraphics.DrawImageメソッドを使うとImageオブジェクトを描画できる
画像の描画に最低限必要となるDrawItemEventArgsクラスのプロパティ
これ以外のプロパティについてはリファレンス・マニュアルを参照していただきたい。

 またDrawItemEventArgsクラスには次のようなメソッドが用意されている。

メソッド名 概要
DrawBackground 項目の背景を背景色で塗りつぶす。選択されている項目に対しては強調表示のための色で塗りつぶされる。このメソッドは画像を描画する前に呼び出す必要がある
DrawFocusRectangle 描画している項目にフォーカスがある場合に、それを示す四角形(枠)を描画する(初期状態などではフォーカスがあっても選択されているとは限らない)
DrawItemEventArgsクラスのメソッド

 通常、DrawItemイベント・ハンドラでは、まずDrawBackgroundメソッドを呼び出し、次に項目の描画を行い、最後にDrawFocusRectangleメソッドを呼び出せばよい。

オーナー描画によりリストボックスに画像を表示するサンプル・アプリケーション

 以下に、冒頭で示したサンプル・アプリケーションのコードを示す。

 このコードを実行するには、Visual Studio .NETでWindowsアプリケーションを新規作成し、ListBoxコントロール、Buttonコントロールをフォームに配置しておく必要がある。そして、配置したButtonコントロールのClickイベントのハンドラとしてbutton1_Clickメソッドを、ListBoxコントロールのDrawItemイベントのハンドラとしてlistBox1_DrawItemメソッドを指定する。

const int spacing = 3; // 画像の周りの余白

// 幅w、高さh内に収まるようなImageオブジェクトを作成
Image createThumbnail(Image image, int w, int h)
{
  float fw = (float)w / (float)image.Width;
  float fh = (float)h / (float)image.Height;

  float scale = Math.Min(fw, fh);
  int nw = (int)(image.Width * scale);
  int nh = (int)(image.Height * scale);

  return new Bitmap(image, nw, nh);
}

// ListBoxのDrawItemイベントのハンドラ
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
  if (e.Index == -1) // 項目がない場合にも呼び出される
    return;

  e.DrawBackground();

  Image thumbnail = (Image)listBox1.Items[e.Index];
  // 画像を中央に表示
  e.Graphics.DrawImage(thumbnail,
    e.Bounds.X + (e.Bounds.Width - thumbnail.Width) / 2,
    e.Bounds.Y + (e.Bounds.Height - thumbnail.Height) / 2);

  e.DrawFocusRectangle();
}

// ButtonのClickイベントのハンドラ

private void button1_Click(object sender, EventArgs e)
{
  // プロパティ設定
  listBox1.ItemHeight = 100;
  listBox1.ScrollAlwaysVisible = true;
  listBox1.DrawMode = DrawMode.OwnerDrawFixed;

  string imageDir = @"c:\images"; // 画像ディレクトリ
  string[] images = System.IO.Directory.GetFiles(imageDir, "*.jpg");

  foreach (string s in images)
  {
    Image original = Image.FromFile(s);
    Image thumbnail = createThumbnail(
      original,
      listBox1.ClientSize.Width - spacing * 2,
      listBox1.ItemHeight - spacing * 2);

    listBox1.Items.Add(thumbnail); // 画像の追加

    original.Dispose();
    // thumbnailオブジェクトは破棄できない
  }
}
Const spacing As Integer = 3 ' 画像の周りのスペース

' 幅w、高さh内に収まるようなImageオブジェクトを作成
Function createThumbnail(ByVal image As Image, ByVal w As Integer, ByVal h As Integer) As Image
  Dim fw As Double = CDbl(w) / CDbl(image.Width)
  Dim fh As Double = CDbl(h) / CDbl(image.Height)

  Dim scale As Double = Math.Min(fw, fh)
  Dim nw As Integer = CInt(image.Width * scale)
  Dim nh As Integer = CInt(image.Height * scale)

  Return New Bitmap(image, nw, nh)
End Function


' ListBoxのDrawItemイベントのハンドラ
Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
  If e.Index = -1 Then ' 項目がない場合にも呼び出される
    Return
  End If

  e.DrawBackground()

  Dim thumbnail As Image = CType(ListBox1.Items(e.Index), Image)
  ' 画像を中央に表示
  e.Graphics.DrawImage(thumbnail, _
    e.Bounds.X + (e.Bounds.Width - thumbnail.Width) \ 2, _
    e.Bounds.Y + (e.Bounds.Height - thumbnail.Height) \ 2)

  e.DrawFocusRectangle()
End Sub

' ButtonのClickイベントのハンドラ
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  ' プロパティ設定
  ListBox1.ItemHeight = 100
  ListBox1.ScrollAlwaysVisible = True
  ListBox1.DrawMode = DrawMode.OwnerDrawFixed

  Dim imageDir As String = "c:\images" ' 画像ディレクトリ
  Dim images As String() = System.IO.Directory.GetFiles(imageDir, "*.jpg")

  For Each s As String In images
    Dim original As Image = Image.FromFile(s)
    Dim thumbnail = createThumbnail(original, _
      ListBox1.ClientSize.Width - spacing * 2, _
      ListBox1.ItemHeight - spacing * 2)

    ListBox1.Items.Add(thumbnail) ' 画像の追加

    original.Dispose()
    ' thumbnailオブジェクトは破棄できない
  Next
End Sub
オーナー描画によりリストボックスに画像を表示するコード(上:C#、下:VB.NET)

 このコードでは、ListBoxコントロールのScrollAlwaysVisibleプロパティをtrueにして常時リストボックスのスクロールバーを表示している点に注意してほしい。このプロパティがfalseの場合、リストボックス内の項目に応じて自動的にスクロールバーが表示されるのだが、そうするとDrawItemイベント・ハンドラで描画する画像のサイズがスクロールバーの有無により一定にならない。

 ここでは最初からスクロールバーを表示してリストボックス内の領域を固定幅にすることにより、表示する画像のサムネイル画像を項目の追加時に作成できるようにしている。このようにすることにより、オリジナルの画像をメモリ上から破棄(Disposeメソッドを呼び出す)できる。これは大きな画像を扱うときにメモリの節約になる。

 なおデフォルトではListBoxコントロールのIntegralHeightプロパティがtrueとなっているため、ListBoxコントロールの高さはItemHeightプロパティの倍数に自動的に調節される。IntegralHeightプロパティをfalseにすればコントロールの高さを任意に設定できる。End of Article

カテゴリ:Windowsフォーム 処理対象:ListBoxコントロール
使用ライブラリ:ListBoxコントロール(System.Windows.Forms名前空間)
使用ライブラリ:DrawItemEventArgsクラス(System.Windows.Forms名前空間)
使用ライブラリ:DrawMode列挙体(System.Windows.Forms名前空間)
関連TIPS:ListBoxコントロールのオーナー描画(高さ可変)により画像を一覧表示するには?
 
この記事と関連性の高い別の.NET TIPS
ListBoxコントロールのオーナー描画(高さ可変)により画像を一覧表示するには?
文字列や画像を無効状態で描画するには?
[ASP.NET AJAX]ListSearchコントロールで検索可能なリストを作成するには?
Windowsフォームで簡単に画像を表示するには?
コントロールの項目を高速に追加/変更/削除するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

Insider.NET 記事ランキング

本日 月間
ソリューションFLASH