お題
Delphi VCL で カスタムコンポーネントをつくっていると、独自のイベントを作りたくなりますよね? ね? ね?
イベントを発生したら、イベントハンドラに色々とパラメータを渡ししたいですよね?
今回は、イベント型をつくって、使う方法のメモになります。
準備
今回もコンポーネントをインストールしないで試します。
なので、まず、親になるVCLアプリケーションを作ります。
ファイル > 新規作成 > Windows VCLアプリケーション でアプリを作ります。
のっぺらぼーのフォームが表示されます。
テストデータを表示するため、Panelをひとつだけ貼り付けてください。
1 | 2 |
---|---|
フォームの名前 | Form1 |
ファイルの名前 | Unit1.pas |
こんな感じで、適当なフォルダーに保存。
コンポーネント > コンポーネントの新規作成 から新規コンポーネントを作ってください。
コンポーネントの派生元は、TCustomControl を選択します。
新規に作るコンポーネントの名前は、とりあえず TCustomEvCpt にしています。
1 | 2 |
---|---|
派生元 | TCustomControl |
新規コンポーネントの名前 | TCustomEvCpt |
ファイルの名前 | CustomEvCpt.pas |
インストールするを選択しません。
新規作成を選択します。
フォームと、同じフォルダーに保存します。
コンポーネント側
まずコンポーネント側から始めます。
イベント型を宣言します。
T○○○○Event = procedure (Sender:TObject; var 渡すパラメーター :型) of Object;
と、いう書き方になります。
Sender:TObject は、必要です。これがないと、あとでメインフォームから呼び出そうとしたときに、パラメーター不一致でエラーになっちゃいますから。
type //イベント型 イベント ハンドラ手続きを指すメソッド ポインタ★
TShikenEvent = procedure (Sender:TObject; var num1,num2:Longint) of object;
カスタムコンポーネントを宣言します。
type
TCustomEvCpt = class(TCustomControl)
private
{ Private 宣言 }
FOnTestEvent:TShikenEvent; //Forceを選択するイベント型のフィールド★
protected
{ Protected 宣言 }
public
{ Public 宣言 }
procedure Paint; override; //見えないとマウスでポチれないからね
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
published
{ Published 宣言 }
property OnMouseDown; //イベント発生の場所として設定
property OnTestEvent:TShikenEvent read FOnTestEvent write FOnTestEvent;
end;
private 宣言のところに、イベント型のポインタを保持するフィールドを宣言します。
FOn◇◇◇Event : T○○○○Event;
という書き方になります。
イベントと、さっき作ったイベント型を結びつけます。
イベントを宣言します。
published 宣言に property としてイベントを宣言します。
イベントって、実は、property なんです。
property On◇◇◇Event : T○○○○Event read FOn◇◇◇Event write On◇◇◇Event;
つまり
property Onイベントの名前 : イベント型 read Fonフィールド write Fonフィールド;
という書き方になります。
published
{ Published 宣言 }
property OnTestEvent:TShikenEvent read FOnTestEvent write FOnTestEvent;
end;
実装部
コンポーネント側で実際に、イベントを発生させる処理です。
ポイントは、
if Assigned(FOnTestEvent) then
と、評価してから、
OnTestEvent(Self,Dat1,Dat2);
と、イベント発生を行うこと。
Assigned( )は、ポインタ変数や手続き変数が nil(代入されていない)かどうかを検査します。
なぜ、検査する必要があるかというと……
メインフォーム側でイベントハンドラを用意しているとは、限らないからです。
if Assigned(FOnTestEvent) then の値は、メインフォーム側がイベントハンドラを使っていないときは、False になります。
procedure TCustomEvCpt.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Assigned(FOnTestEvent) then //イベント発生
OnTestEvent(self,x,y);
inherited;
end;
Senderには、Selfを入れます。
作例として、Mouse座標 X,Y をイベントハンドラへ渡しています。
以上で、コンポーネント側の準備は完了です。
つぎは、呼び出して使う側のメインフォームでの処理になります。
メインフォーム側
uses節に書き足し
まず、先ほど作ったカスタムコンポーネントを、Uses節の末尾に書き足します。
とりあえず「CustomEvCpt.pas」というファイル名で、同じフォルダーの中に入れたのでこうなります。
拡張子のPasは省略です。
PATHが通すの、めんどくさいときは、とりあえず同じフォルダにファイルを保存で良いと思います。
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,CustomEvCpt;
カスタムコンポーネントと独自イベントを宣言
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
Nanika:TCustomEvCpt;
public
{ Public 宣言 }
procedure TestEvent (Sender:TObject; var num1,num2:Longint);
end;
コンポーネントを作って、使って、ポイ
イベントも、コンポーネントを作成時に、一緒に定義します。
TCustomEvCptクラスのコンポーネントで、名前は Nanika です。
procedure TForm1.FormCreate(Sender: TObject);
begin
Nanika:=TCustomEvCpt.Create(Self); //コンポーネント作成
with Nanika do
begin
parent:=self; //コンポーネントには親が必要です
left:=0;
top:=0;
width:=100;
height:=100;
align:=alClient;
OnTestEvent:=TestEvent; //イベントを宣言★
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Nanika.free;
end;
以上で、カスタムコンポーネントを作って、独自イベントを発生させる準備が整いました。
使うときは、ふつうにこんな風にします。
procedure TForm1.TestEvent (Sender:TObject; var num1,num2:Longint);
begin
Panel1.caption:='Mouse X:'+inttostr(num1)+' Y:'+inttostr(num2);
end;
実行します
[F9]キーでコンパイルして、実行します。
赤い枠で、コンポーネントが表示されているため、マウスでポチって下さい。
Panle1に、mouse 101 202 と、テスト用のデータが表示されます。
図にまとめてみます
あとがき
Senderを使わないから、これを省略したら、
[dcc32 エラー] Unit1.pas(41): E2009 型に互換性がありません : パラメータリストが異なります。
って、コンパイラさんに怒られて、原因がわからず、ネットを探し回るハメになりました。
OnTestEvent:=TestEvent;
ここで、文字列の上にマウスを載せると、パラメーターが表示されます。
左辺の OnTestEvent が、コンポーネント側で、
右辺の TestEvent が、メインフォーム側です。
何気なく、マウスを載せたら…… あれ? 右辺にだけSenderがあるって、気づいたのでした。
苦労したので、めもめも。
参考
dowiki の System.Assigned の説明ページ
https://docwiki.embarcadero.com/Libraries/Sydney/ja/System.Assigned
プログラム全文を乗せます。
▼▼▼ クリックして 開いてください ▼▼▼
Unit1.pas プログラム全文 [クリックで開いてください]
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,CustomEvCpt;
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
Nanika:TCustomEvCpt;
public
{ Public 宣言 }
procedure TestEvent (Sender:TObject; var num1,num2:Longint);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Nanika:=TCustomEvCpt.Create(Self);
with Nanika do
begin
parent:=self;
left:=0;
top:=0;
width:=100;
height:=100;
align:=alClient;
OnTestEvent:=TestEvent;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Nanika.free;
end;
procedure TForm1.TestEvent (Sender:TObject; var num1,num2:Longint);
begin
Panel1.caption:='Mouse X:'+inttostr(num1)+' Y:'+inttostr(num2);
end;
end.
CustomEv.pas プログラム全文 [クリックで開いてください]
unit CustomEvCpt;
//カスタムコンポーネントに独自イベントを設定する方法
interface
uses
System.SysUtils, System.Classes, Vcl.Controls,Vcl.Graphics;
type //イベント型 イベント ハンドラ手続きを指すメソッド ポインタ★
TShikenEvent = procedure (Sender:TObject; var num1,num2:Longint) of object;
// TShikenEvent = procedure (var num1,num2:Longint) of object;
//Sender:TObjectは必要です。パラメーター不一致になるから。
type
TCustomEvCpt = class(TCustomControl)
private
{ Private 宣言 }
FOnTestEvent:TShikenEvent; //Forceを選択するイベント型のフィールド★
protected
{ Protected 宣言 }
public
{ Public 宣言 }
procedure Paint; override; //見えないとマウスでポチれないからね
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
published
{ Published 宣言 }
property OnMouseDown; //イベント発生の場所として設定
property OnTestEvent:TShikenEvent read FOnTestEvent write FOnTestEvent; //★フィールドです
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TCustomEvCpt]);
end;
procedure TCustomEvCpt.Paint;
begin
with canvas do
begin
pen.color:=clRed; //MouseDownでイベント発生にしているため
rectangle(left,top,width,height); //ポチるために、コンポーネントを描きます
end;
inherited;
end;
procedure TCustomEvCpt.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Assigned(FOnTestEvent) then //イベント発生
OnTestEvent(self,x,y);
inherited;
end;
end.