5月に入って、WindowsというOSの特徴であるGUI、その基礎となる描画等、色々ととりとめもなく描いてきましたが、前回Windows(SDK)における「再描画しても消えないユーザーによる描画作法」(注1)、その面倒を軽減するためのBCCSkeltonのCANVASクラスの仕組み(注2ビットマップを使った仮想ウィンドウ)を語り、C#においてもFormクラスでは矢張りOnPaintメソッド(注3)で描画処理を行うことが基本であることを書きました。
注1:ウィンドウが無効領域を認知すると発するWM_PAINTメッセージを受けて、BiginPaint、EndPaint両関数の間に描画処理を行う。
注2:いつでも描画処理ができるようにウィンドウのクライアント領域に貼る「画版(CANVAS)」をビットマップで作り、そのCANVASに書き込み、(InvalidateRect関数等で)無効化処理を行ってWM_PAINTを呼び出し、WM_PAINTにおける描画処理を「CANVASビットマップを貼り付ける」だけにする。
注3:Formクラスから派生するControlクラスでも同じくOnPaintメソッドがあります。ユーザーがここで処理を行う為には"override"するか、イベントハンドラーで処理を追加する必要があります。
又、イメージを扱うPictureBoxというコントロールがあることと、GDIによる描画処理を扱うGraphicsというクラスがあるようなので、それを調べてみたところ、
(1)PictureBox
Formから派生したコントロールで、特徴はこのコントロールに表示させるImageクラスのプロパティを有していることです。しかし、このImageプロパティに直接描画するようなメソッドは見当たりません。
(2)Graphics
GDI(含GDI+)に基づく描画処理を担うクラスで、実に多彩なプロパティと多様な(実際、私如きでは使いきれない感じです-泣;)描画メソッドを持っています。
(3)PictureBoxへの描画
PictureBoxがImageプロパティを持っているので、これにビットマップ等イメージを貼り付けることで簡単に描画できます。(恐らく↑の注2で述べた仮想ウィンドウの表示と同じような処理をカプセル化して、このImageプロパティへのsetでやっているのでしょう。)従って、「線を引く」「楕円(含真円)を描く」等の描画処理はImageプロパティへ貼り付けるビットマップ等の画像で行うことで「再描画しても消えない描画」ができることになると思われます。実際、webでサンプルコードを見てみると、以下のような感じでした。
//ビットマップを作成
Bitmap bmp = new Bitmap(幅、高さ); //幅と高さ指定だけの「無地」ビットマップを作成するコンストラクター
//ビットマップのGraphicクラスインスタンスを作成
Graphics g = Graphics.FromImage(bmp);
//Graphicsオブジェクトgを使った描画例
//位置(200, 300)に100x80の四角を赤色で描く
g.DrawRectangle(Pens.Red, 200, 300, 100, 80);
//Graphicsオブジェクトの開放(注)
g.Dispose();
//ピクチャーボックス(PictureBox picBox)へ表示
picBox.Image = bmp;
//Bitmapオブジェクトの開放は必要か?-Visual Studioを使っているサンプルでは書かれていない(注)
bmp.Dispose(); //必要?
注:GraphicsクラスオブジェクトはDC(Device Context)を持っているのでDisposeによる明示的メモリー開放が必要になるのはわかります。また、描画関係ではPenクラス、Brushクラスのオブジェクトもユーザーが明示的にDisposeしてメモリーを開放する必要があるようです。しかし、サンプルのコードにはBitmapクラスオブジェクトbmpはDisposeしていません。Bitmapクラスがガーベージコレクションの対象になるのかどうか、調べた人がいました。一応メモリーリーク的は起こりますが、やばい状況になるとガーベージコレクションが働くようですね。しかし、「使い終わったらDispose」を推奨しています。ここにもDispose派がいますね。この人もそうです。Bitmapではなく、その基底になるImageクラスですが、Microsoftもこう言っています。(「Dispose を使い終わったら Image を呼び出します。」は「Image を使い終わったら Dispose を呼び出します。」の翻訳誤りですね。)C++では「newしたら、delete」、「Createしたら、DeleteまたはDestroyあるいはRelease」が当たり前ですが、C#でも描画系オブジェクトはDisposeしておいた方が無難のようですね。(しかし、delete、destroy、releaseのC++とDispose、FinalizeのC#では用語を敢えて変えている印象がありますね。)
ここまで調べたことの「まとめ」として、(C#は多様、多彩なクラス、コンポーネントクラスがあるので、自分勝手にクラスを作るのはご法度なんでしょうが)C#でもBCCSkeltonのCANVASクラスのようなものを欲しくなってしまいました。
ということで、次からは現在進行形のPictureBox用描画ツールクラス"Easel"(注)について書いてゆきます。
注:まぁ、大したことをするクラスじゃないので飽くまでC#学習の一環として「習作」であることをお断りしておきます。また名称については、CanvasクラスやScreenクラスなどが既に存在するので、現在のクラスにない名称として(「Canvas「画版」を置くならEasel「画架」だろう?」という発想で命名しました。"C# Easel"でググっても何も出てきませんので大丈夫かと。)
