4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Delphi Vclでカスタムコンポーネントに、独自設定のイベントを付けるメモ

Last updated at Posted at 2025-02-16

お題

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 と、テスト用のデータが表示されます。

図にまとめてみます

イベント図解1.png

イベント図解2.png

あとがき

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.

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?