Delphi Programming / Object Pascal
[掲載 2012年07月17日] [更新 2012年07月17日] Delphi サンプルプログラム集
082_TPanel 等の複数行表示と protected 部へのアクセス
動作確認等
Windows XP(SP3) + Delphi XE Pro
Windows 7 U64(SP1) + Delphi XE Pro
082_TPanelMultipleLines.zip [2,176 KB] 2012年07月17日版 (EXE 同梱)
TPanel 等の複数行表示と protected 部へのアクセス
TPanel や TSpeedButton 等のコントロールの Caption を複数行にして表示するための参考用サンプルです.protected 部にアクセスするための参考コードも掲載しています.
ここでは,対象コントロール (コンポーネント) を,TSpeedButton にしています.
01_ sLineBreak ( 改行文字 ) による複数行表示
次のコードのように,文字列の途中に改行文字を挿入すれば,複数行表示となります.
このコードでは,ボタンをクリックすると複数行表示するようにしています.
図1
設計時画面
図2
実行結果
リスト1
sLineBreak ( 改行文字 ) による複数行表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
type
TForm1 = class (TForm)
SpeedButton1: TSpeedButton;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// TSpeeButtonに複数行表示(TPanelでも同じ方法で可能)
// 文字列を改行文字で結合する方法
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
SpeedButton1.Caption := '123' + sLineBreak + 'ABC' ;
end ;
end .
02_ Canvas への描画と protected 部へのアクセス
上のサンプルでは,行間はシステムによって決まってしまいます.また,文字列の描画位置も限られています.
行間を任意の値にしたり,表示位置を自由に変更するには,コントロールの Canvas に描画します.ただし,TPanel 等の Canvas プロパティは,protected 部で定義されているため,そのままではアクセスできません.そこで,TPanel を継承したクラスを定義し,派生クラスでキャストして protected 部にアクセスするようにしたのが次のサンプルです.
継承クラスを定義すると,元のクラスの protected 部のメンバ,つまりプロパティやメソッドが使えるようになります.新規にプロジェクトを作成すると,TForm の protected 部のメンバーも使用可能となっています.それと同じ原理です.
より新しいバージョンの Delphi では,以前は protected 部にあったメンバーが publuc, published 部に移動していることもあります.
ただし,この方法では,SpeedButton の再描画が行われると,描画した文字列が消えてしまいます.
図3
設計時画面
図4
実行して Button1 をクリック
図5
マウスを移動
マウスがくると文字列が消えてしまう
他のウィンドウが上に重なっても消えてしまう
リスト2
Canvas への描画と protected 部へのアクセス
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
type
TForm1 = class (TForm)
SpeedButton1: TSpeedButton;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// protected部にアクセスするためのクラス定義
// インチキでもなんでもない
type
TMySpeedButton = class (TSpeedButton);
// =============================================================================
// CanvasのTextOutやWin32APIのDrawTextを使用してCanvasに描画する方法
// この方法なら任意の位置にテキストや画像を表示可能
// ただし,永続的には表示できない
//
// コンポーネントとして実装するにはPaintメソッド等を使用する
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
// SpeedButton1をTMySpeedButtonにキャストする
TMySpeedButton(SpeedButton1).Canvas.TextOut(20 , 10 , '123' );
TMySpeedButton(SpeedButton1).Canvas.TextOut(20 , 30 , 'ABC' );
end ;
end .
上のサンプルコードでは,TSpeedButton に他のウインドウやマウスが移動してくると,折角描画した文字列が消えてしまいます.TPaintBox への描画と同じ現象です.
protected 部へのアクセスは便利ですが,描画関係はこのような問題があります.これを避けるには,元のコンポーネントの Paint メソッドをオーバーライドした継承コンポーネントを作成し,Paint メソッド内で Canvas に描画します.
Paint メソッドは,再描画が必要な時に自動的に実行されるので,描画した内容が消えることはありません.次のサンプルでは,コンポーネントをインストールするのは面倒なので,実行時に生成してテストしています.
このコンポーネントでは,Caption をコンポーネント内部で設定してしまっています.実用的な意味では,外部から操作可能な public や published なプロパティにしておくべきでしょう.
文字列配列をプロパティとしてもいいし,改行を含む文字列をプロパティとして,コンポーネントの内部で改行文字を判定して複数行にする方法も考えられます.
プロパティやメソッド類の実装方法は,他のコンポーネント類のコードを参考にしてください.
[備考]
DrawText 関数,DrawTextEx 関数を使用すれば改行を含む文字列を処理できますが,これらの関数には行間を調整する機能はありません.
TextOut 関数を使用する場合,改行文字で分割して複数行にする方法も考えられます.
図6
設計時画面
図7
実行結果
リスト3
複数行機能付きのコンポーネントを生成して使用する
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
type
TMySpeedButton = class (TSpeedButton)
protected
procedure Paint; override ;
end ;
TForm1 = class (TForm)
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
MySpeedButton1 : TMySpeedButton;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// フォームの生成時に,複数行表示のTPanelやTSpeedButtonを生成する
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
MySpeedButton1 := TMySpeedButton.Create(Self);
MySpeedButton1.Parent := Self;
MySpeedButton1.Left := 24 ;
MySpeedButton1.Top := 55 ;
MySpeedButton1.Width := 145 ;
MySpeedButton1.Height := 49 ;
end ;
{ TMySpeedButton }
// =============================================================================
// 複数行表示機能のコンポーネントのPaintメソッド(のoverride)
// Paintメソッドは自動的に実行される
// =============================================================================
procedure TMySpeedButton.Paint;
begin
inherited ;
Canvas.TextOut(20 , 10 , '123' );
Canvas.TextOut(20 , 30 , 'ABC' );
end ;
end .
04_ 複数行表示機能付きのコンポーネントを実行時に置き換える
コンポーネントをインストールしないでテストする別の方法です.
派生コンポーネントの型の変数を宣言 (定義) して,実行時に,フォームに配置したコンポーネントを継承したコンポーネントと置き換えて使用します.そのために,以下のリンクの記事で説明しているユニットを利用しています.このユニットのソースコードは,サンプルのプロジェクに添付しています.
図8
設計時画面
フォームに配置してある SpeedButtonを,実行時に派生コンポーネントに入れ換える
図9
実行結果
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
type
TMySpeedButton = class (TSpeedButton)
protected
procedure Paint; override ;
end ;
TForm1 = class (TForm)
SpeedButton1: TSpeedButton;
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
// 派生コンポのオブジェクトを定義
MySpeedButton1 : TMySpeedButton;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
plChgCompo;
{$R *.DFM}
// =============================================================================
// フォームの生成時にコンポーネントを置き換える
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
// フォーム作成時にコンポーネントの入れ替え
// フォーム作成時にコンポーネントの入れ替え
MySpeedButton1 := TMySpeedButton(ChangeComponent(SpeedButton1, TMySpeedButton));
end ;
{ TMySpeedButton }
// =============================================================================
// 複数行表示機能のコンポーネントのPaintメソッド(のoverride)
// Paintメソッドは自動的に実行される
// =============================================================================
procedure TMySpeedButton.Paint;
begin
inherited ;
Canvas.TextOut(20 , 10 , '123' );
Canvas.TextOut(20 , 30 , 'ABC' );
end ;
end .
派生コンポーネントのプロパティを使用する例です.
このサンプルでは,派生コンポーネントに Text という String 型のプロパティを実装しています.
図10
実行結果
リスト5
実行時にコンポーネントを置き換える時のプロパティの設定方法
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
type
TMySpeedButton = class (TSpeedButton)
private
FText : String ;
protected
procedure Paint; override ;
public
property Text : String read FText write FText;
end ;
TForm1 = class (TForm)
SpeedButton1: TSpeedButton;
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
// 派生コンポのオブジェクトを定義
MySpeedButton1 : TMySpeedButton;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
plChgCompo;
{$R *.DFM}
// =============================================================================
// フォームの生成時にコンポーネントを置き換える
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
// フォーム作成時にコンポーネントの入れ替え
MySpeedButton1 := TMySpeedButton(ChangeComponent(SpeedButton1, TMySpeedButton));
MySpeedButton1.Text := '東京都' ;
end ;
{ TMySpeedButton }
// =============================================================================
// 複数行表示機能のコンポーネントのPaintメソッド(のoverride)
// Paintメソッドは自動的に実行される
// =============================================================================
procedure TMySpeedButton.Paint;
begin
inherited ;
Canvas.TextOut(20 , 10 , FText);
end ;
end .