連載 .NETでWindowsアプリを作ろう

第2回 サムネイル画像コントロールを作ろう

デジタルアドバンテージ 遠藤 孝信
2005/07/23
Page1 Page2 Page3 Page4

■オーバーライド・メソッドの追加

 次にサムネイルの描画処理について見ていきます。

 コントロールでは、サムネイルを描画しなければならないタイミングがいくつかあるのですが、その1つはOnPaintメソッドが呼び出されたときです。このメソッドは、コントロールがリサイズされたり、コントロールを覆っていたほかのウィンドウが移動されたりして、表示領域の再描画が必要になると自動的に呼び出されます。

 実際に呼び出されるOnPaintメソッドは基本クラスであるUserControlクラスのOnPaintメソッドですが、これをコントロール(ThumbViewerクラス)でオーバーライドすることにより、コントロール独自の描画処理を追加できます。

 まずはVS.NETの機能を使って、OnPaintメソッドのひな型を作成しましょう。C#では「override 」と入力すれば、オーバーライド可能なメソッドの一覧が表示され、メソッドを選択するとそのメソッドのひな型が作成されます。

C#におけるオーバーライド・メソッドの作成
VS.NETの機能を使って、OnPaintメソッドのひな型を作成しているところ。
  「override 」と入力(「override」の後にスペースが必要)。
  オーバーライドするメソッドを選択。

 VB.NETでは、エディタ画面左上にあるリストボックスから「(Overrides)」を選択し、その右側のリストボックスでオーバーライドするメソッドを選択すると、メソッドのひな型が作成されます。

VB.NETにおけるオーバーライド・メソッドの作成
VS.NETの機能を使って、OnPaintメソッドのひな型を作成しているところ。
  「(Overrides)」を選択。
  オーバーライドするメソッドを選択。

■OnPaintメソッドによるサムネイルの描画

 それではOnPaintメソッドの内容を見ていきましょう。サムネイルを表示するための本コントロールはサムネイルの描画速度が命ですので、いかに無駄な描画を抑えるかが鍵になります。

 まず当然ながら、コントロールの表示領域に収まっていない(スクロールしないと見えないような)サムネイルは描画する必要がありません。

 ポイントとなるのはサムネイルのスクロール時です。このときには画面全体の内容が変化しますが、スクロールにより(上あるいは下に)移動した「すでに描画済みのサムネイル部分」はユーザー・コントロールの機能により自動的に描画されます。このため、その部分はプログラムから描画する必要がありません。

 再描画が必要となる部分は、スクロールにより新たに表示領域に現れる「未描画のサムネイル部分」です。この部分は、OnPaintメソッドの第1パラメータであるPaintEventArgsオブジェクトのClipRectangleプロパティにより、その位置(X、Y)とサイズ(Width、Height)を得ることができます(この部分は「無効領域」と呼ばれます*)。

* OnPaintメソッドでは無効領域以外の部分に描画しても画面には反映されません。
 
スクロール時に描画が必要な領域(サムネイルを下方向にスクロールさせた場合)

 またこの図にもあるように、現在どのくらいスクロールしているかはコントロールのAutoScrollPositionプロパティから知ることができます。なおこのプロパティの値は常に負の値となるので注意してください。

 OnPaintメソッドの実際の内容は次のようになります。

protected override void OnPaint(PaintEventArgs e)
{
  base.OnPaint (e);

  int rowStart = (-this.AutoScrollPosition.Y + e.ClipRectangle.Y) / gridSize;
  int rowEnd = (-this.AutoScrollPosition.Y + e.ClipRectangle.Y + e.ClipRectangle.Height) / gridSize + 1;

  for (int i = rowStart * cols ; i < Math.Min(rowEnd * cols , images.Count); i++)
  {
    drawOne(e.Graphics, i, i == selectedIndex);
  }
}
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

  MyBase.OnPaint(e)

  Dim rowStart As Integer = (-Me.AutoScrollPosition.Y + e.ClipRectangle.Y) \ gridSize
  Dim rowEnd As Integer = (-Me.AutoScrollPosition.Y + e.ClipRectangle.Y + e.ClipRectangle.Height) \ gridSize + 1

  Dim i As Integer
  For i = rowStart * cols To Math.Min(rowEnd * cols, images.Count) - 1
    drawOne(e.Graphics, i, i = _selectedIndex)
  Next
End Sub
サムネイルの描画を行うOnPaintメソッド(上:C#、下:VB.NET)

 ここではまず、描画を行う開始行(変数rowStart)と最終行(変数rowEnd)を求めます。そして、それらの行に含まれるサムネイルをdrawOneメソッドにより描画します。

 drawOneメソッドは、第2パラメータで示されたインデックス番号のサムネイルを表示します(インデックス番号は、左上隅のグリッドを0番とした場合のサムネイルの位置です)。第3パラメータでは、サムネイルの青枠を表示するかどうかをbool値により指定します。selectedIndexフィールド(VB.NETの場合は_selectedIndex)は現在選択されているサムネイルのインデックス番号を示しています。

 drawOneメソッドは、最終的にはGraphicsオブジェクト(e.Graphics)のDrawImageメソッドを使って、サムネイルのビットマップを描画します。しかし、実際の描画については細かな座標計算ばかりとなっているため、本稿での解説は大幅に割愛させていただきます。

 さて、スクロール時の描画はこのようにして最小限に抑えることができますが、コントロールのリサイズ時とサムネイルのサイズ変更時(ThumbnailSizeプロパティが変更された場合)には、コントロールの仕様上、表示領域全体を再描画するしかありません。

 これらの再描画は、コントロールのリサイズ時に呼び出されるOnResizeメソッドと、ThumbnailSizeプロパティのSetアクセサ定義部分で、コントロールのInvalidateメソッドを呼び出すことにより行っています(invalidateは「無効にする」という意味)。このメソッドを呼び出すと表示領域全体が無効領域となり、メソッド呼び出し後にはOnPaintメソッドが呼び出されます。


 INDEX
  .NETでWindowsアプリを作ろう
  第2回 サムネイル画像コントロールを作ろう
    1.独自コントロールとして作成するサムネイル画像コントロール
    2.プロジェクトの作成とグリッドの設定
  3.OnPaintメソッドによるサムネイルの描画
    4.画像の追加とマウス・クリック時の処理
 
インデックス・ページヘ  「.NETでWindowsアプリを作ろう 」


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メールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間