Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2015年09月16日] [更新 2015年09月16日]* Delphi 一般・その他
カスタム描画における TCanvas ( 入門者用 )
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
[整理番号 - 0120]







01_カスタム描画とは


コンピュータの画面に表示されているものは、全て文字列や画像、あるいは線や図形等を「描画」した結果として表示されています。
Delphi にも描画のための機能が実装されています。

新規にプロジェクトを作成して、以下のように操作します。

  • (1) TStringGrid コンポーネントと TButton コンポーネントをフォームに配置する
  • (2) Button1 のクリックイベントを以下のコードにする

procedure TForm1.Button1Click(Sender: TObject);
begin
  StringGrid1.Cells[1,1] := '100';
end;


コンパイルして実行し、Button1 をクリックすると下図のようになります。
これが、TStringGrid のセルに値を代入した時のデフォルトの描画です。


図1
TStringGrid における描画の例
  • セルに値 (文字列に擬る) を代入すると、自動的にセルにその値が描画される
  • これがデフォルトの描画で、セルの左上から文字列が描画 (表示) される

セルに文字列を描画する際、表示位置や文字列のサイズ等を指定したり、セルの背景の色を変更したい場合があります。
下図はその例です。デフォルトの描画ではなく、描画、あるいは表示等をカスタマイズすると言う意味で、これをカスタム描画と言うことがあります。


図2
TStringGrid におけるカスタム描画の例
  • デフォルトの描画はしていない
  • セル上下中央で、右寄せで描画
  • フォント (文字) の色とサイズも変更している





02_描画は TCanvas のメソッドやプロパティを使用する


Delphi のビジュアルなコンポーネント、コントロールの多くは、Canvas というプロパティを持っています。線や他の図形を描いたり、画像を表示するのも、全て、この Canvas に描画します。
Canvas (キャンバス、カンバス) と言う用語は、いろいろな場面で使われています。油絵を描く布もキャンパスと呼ばれています。

Canvas プロパティは、TCanvas という型のクラス型のプロパティです。クラス型ですから、プロパティやメソッドがあります。TCanvas には、直線を描いたり、円等の図形を描画するメソッド、画像描画用のメソッド等が実装されています。
TCanvas には Font というプロパティもあり、この Font プロパティもまた、TFont というクラス型のプロパティになっています。TFont は文字通り、文字を描画する時に使用するフォントを設定するためのクラスです。

次のコードは、TCanvas, TFont の機能を確認するための簡単なテストです。
新規にプロジェクトを作成したら、TButton コンポーネントをフォームに配置します。この TButton の OnClick イベントを次のようにします。

文字列は Canvas に描画します。したがって、フォントは TForm1.Font ではなく、TCanvas.Font に対して設定します。塗り潰しの色とパターンを設定する TBrush や、描画に使用するペンの種類を設定する TPen も同じように、TCanvas.Brush, TCanvas.Pen で設定します。



リスト1
//-----------------------------------------------------------------------------
//  TCanvasへの描画の例とフォントの設定例
//  TCanvas.Fontの設定は文字を描画するフォントの設定となる
//-----------------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
  //Form1のフォントではなくForm1のCanvasのFontを設定する
  Canvas.Font.Size := 17;
  //Form1のCanvasにテキストを描画する
  Canvas.TextOut(10, 40, 'Delphi Programming');

  //フォント名(文字列の描画に使用するフォント名)を変えてみる
  Canvas.Font.Name := 'Courier New';
  //フォントの色(文字列の色)を変えてみる
  Canvas.Font.Color := clBlue;
  //Form1のCanvasにテキストを描画する
  Canvas.TextOut(10, 90, 'Delphi Programming');
end;


下図が実行結果です。



図3
上のコードの実行結果
  • TCanvas には各種図形描画用のメソッドもある
  • TCanvas には画像描画用のメソッドもある

次のコードは、Parrots.bmp というビットマップ画像をディスクから読み出して TCanvas に描画する例です。その下の図が実行結果です。



リスト2
//-----------------------------------------------------------------------------
//  TCanvasにビットマップ画像を描画する例
//-----------------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
  LImagePath : String;
  LBitMap    : TBitmap;
begin
  LImagePath := ExtractFilePath(Application.ExeName) + 'Parrots.bmp';

  LBitMap := TBitmap.Create;
  try
    //ビットマップ画像をディスクから読み込む
    LBitMap.LoadFromFile(LImagePath);
    //ビットマップ画像を描画する
    Canvas.Draw(10, 35, LBitMap);
  finally
    FreeAndNil(LBitMap);
  end;
end;


図4
上のコードの実行結果

このようにして、図形、画像、文字列等を、Canvas に描画できます。
図形と画像と文字列が混在する描画には注意が必要です。文字列を描画した後に、その文字列の領域を含む図形、例えば四角形を描画する時、その四角形の内部を塗り潰してしまうと、先に描画した文字列が見えなくなってしまいます。
そんなことは当たり前だと思うかも知れません。しかし、コミュニティの場では、そのような現象に関する質問が多いのも事実です。

Canvas プロパティを持たないコントロールもあります。Canvas プロパティがないコントロールには、Canvas への描画はできません。
Canvas プロパティがないコントロールで無理やり Canvas プロパティを使用しようとすると、未定義の識別子のエラーが発生してコンパイルできません。


文字列 (テキスト) に限らず、描画の際、TCanvas に実装されているメソッドやプロパティだけではなく、Windows API の描画関係の関数類を使用した方が効率がいい場合もあります。また、Windows API の関数類を使用しないと実現できないような描画もあります。
TCanvas に限らず、Delphi は、Windows が提供している全ての機能を直接使用できるわけではありません。不足する機能は Windows API の関数やメッセージ処理、あるいはタイプライブラリの取り込みで機能を追加して利用します。Delphi には、そのための機能が実装されています。







03_数値は文字列に変換して描画する


アプリケーションでは文字列や画像だけでなく、数値も扱います。
数値は、そのままでは描画できません。文字列に変換して、その文字列を描画します。
以下の記事に、数値を文字列に変換したり、あるいは数値を、書式を整えて文字列に変換する関数やメソッド類の記事へのリンクがあります。文字列だけ、あるいは文字列と数値も含めて指定の書式文字列に変換する関数もあります。
それらの関数やメソッドで処理できない部分は、別途にコーディングすることになります。





04_カスタム描画には描画関係のイベントを使用する


カスタム描画を実装するには、主に Draw という名前が含まれているイベント、あるいは OnPaint イベント等を使用します。
描画に関するこれらのイベントは、何かしらの描画が必要になると自動的に実行されます。ただし、イベントによっては、そのイベントを実装すると、デフォルトの描画を行わないもの、または、デフォルトの描画も行うが、TStringGrid のように、DefaultDrawing プロパティを False にすると行わないよう設定できるものもあります。

利用可能なイベントはコンポーネント (コントロール) によって違います。イベントには各々プロシージャが割り当てられています。例えば TStriingGrid の OnDrawCell イベントには TDrawCellEvent という型のプロシージャが使われます。

イベント内で描画コードを書くときに、これらのイベントの引数を利用することができます。カスタム描画では、このイベントの引数の使い方が重要なポイントとなります。
下表は、Delphi XE(UP1) の TStringGrid のカスタム描画用のイベントです。

当然ですが、描画は対象のコンポーネントやコントロールの Canvas に対して行います。
TStringGrid の場合は、TStringGrid.Canvas ということになります。既に述べたように、フォントやブラシ、ペンの設定も、この TStringGrid.Canvas のプロパティとして設定します。
TStringGrid.Canvas の使用方法は、TCanvas のそれと全く同じです。



イベント名 イベントの型 (引数の型や引数の機能を調べるのに使用)
OnDrawCell TDrawCellEvent


下の表は、Delphi XE(UP1) の TListView コンポーネントのカスタム描画用のイベントです。いくつかのイベントが用意されています。目的によって使い分けることになります。
オンラインヘルプには、C++ の説明しかない記事もありますが、基本的に、どんな引数があり、その引数の型が分かればいいので、十分役に立ちます。

他のコンポーネント (コントロール) のキャンバス同様、TListView.Canvas の使用方法も、TCanvas のそれと全く同じです。



イベント名 イベントの型 (引数の型や引数の機能を調べるのに使用)
OnAdvancedCustomDraw TLVAdvancedCustomDrawEvent
OnAdvancedCustomDrawItem TLVAdvancedCustomDrawItemEvent
OnAdvancedCustomDrawSubItem TLVAdvancedCustomDrawSubItemEvent
OnCustomDraw TLVCustomDrawEvent
OnCustomDrawItem TLVCustomDrawItemEvent
OnCustomDrawSubItem TLVCustomDrawSubItemEvent
OnDrawItem TLVDrawItemEvent


イベントの処理だけでは不足する場合、カスタム描画をメッセージの処理で行う場合もあります。
カスタム描画の実装は、継承クラスを生成したり、派生コンポーネント (派生クラス) を生成して処理する方が便利な場合があります。
派生クラスでは、メソッド類をオーバライド (override : 再定義) して使用することがあります。オーバライドとは、本来の動作に、追加機能分のコードを追加したり、あるいは動作を変更して使用することです。



[備考]
Delphi のオンラインヘルプの記事には、多くの場合、そのイベントで使用するプロシージャの型の説明記事へのリンクがありません。少々不便です。