Sender パラメータ

コンポーネントのイベントハンドラでは、大抵の場合には Sender パラメータがあります。 この Sender パラメータの主な用途は何でしょうか。

ここでは、TButton の OnClick イベントを取り上げて、Sender パラメータについてみていきます。

例えば、Button コンポーネントを一つ貼り付け、OnClick イベントを次のようにします。

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := 'Hello';
  ShowMessage(s);
end;

フォームに貼り付けたボタン(Button1)をクリックすることで、このイベントハンドラが 呼び出されます。

それでは、次にもう一つ Button コンポーネントを貼り付けて下さい。 そして、Button1 の OnClick イベントと同様なコードを書きます。

procedure TForm1.Button2Click(Sender: TObject);
var
  s: string;
begin
  s := 'Hello';
  ShowMessage(s);
end;

このコードは、Button1Click と全く同じです。ですから、コードを実行して、Button1, Button2 どちらのボタンを押しても全く同様のメッセージボックスが表示される事になります。

ところで、複数のコンポーネントには、同じイベントハンドラを共有させることが 可能です。オブジェクトインスペクタで、Button2 を選択し、[ イベント ] ページの OnClick を見てみると、以下の図のように表示されます。

上図の状態で、つまり、Button2 が選択されている状態で OnClick イベントに Button1Click を設定してください。このように設定することで、Button2 がクリックされると、 Button1Click イベントハンドラが呼び出されるようになります。

これを、イベントハンドラの共有と呼びます。このサンプルでは、Button1 を押しても、 Button2 を押しても、「実行されるコード」は同じであるので、イベントハンドラを 共有することで、無駄なコードをなくす事が出来ます。

実際、本当に Button1 と Button2 でイベントハンドラが共有されているかどうかを 確かめる為に、以下のコードを書いてみましょう。

var
  Button1ClickCount,
  Button2ClickCount: Integer;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Inc(Button1ClickCount);
  Label1.Caption := 'Button1: ' + IntToStr(Button1ClickCount);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Inc(Button2ClickCount);
  Label2.Caption := 'Button2: ' + IntToStr(Button2ClickCount);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Label1.Caption := 'Button1: ' + IntToStr(Button1ClickCount);
  Label2.Caption := 'Button2: ' + IntToStr(Button2ClickCount);
end;

Button1, Button2 どちらのボタンを押しても、Button1Click イベントハンドラが 呼び出されています。

イベントハンドラを共有すると、今度は「識別」が必要な場面が出てくる

イベントハンドラを共有(今回のサンプルコードでは、Button1, Button2 で Button1Click イベントハンドラの共有)した場合、別な問題が発生します。それは、 「どのコンポーネントが」イベントを発生させたのかという問題です。

今回の場合、Button1, Button2 でイベントハンドラを共有しましたが、 ボタンを押した際、「Button1, Button2 のどちらがボタンを押したか」を 判別することが出来ません。コードによっては、その隠蔽が有効であることも ありますが、イベント発生の際、呼び出し元のコンポーネントを把握しなければ ならない場合には、困ったことになります。

そんな時に使用できるのが、Sender パラメータです。

この Sender パラメータには、「どのコンポーネントがハンドラを呼び出したか」という情報が格納されています。

まずは、Sender パラメータを用いた簡単なサンプルをみてみましょう。

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := Sender.ClassName;
  ShowMessage(s); // TButton
end;

procedure TForm1.Panel1Click(Sender: TObject);
var
  s: string;
begin
  s := Sender.ClassName;
  ShowMessage(s); // TPanel
end;

Button1 をクリックした場合、TButton と表示されます。また、 Panel1 をクリックした場合には、TPanel と表示されます。

ここで、重要な点は、以下のコードです。

s := Sender.ClassName;

Sender は、TObject 型の変数として宣言されています。TObject クラスでは、 クラスメソッド ClassName が定義されています。このクラスメソッドは、 オブジェクトインスタンスの型を表す文字列を返すメソッドとなっています。

つまり、Sender を利用することで「どのコンポーネントがハンドラを呼び出したのか」を識別することができます。

次の例では、Button コンポーネントを 2 つ用いて、押された方のボタンの Caption を ラベル(Label1)に表示させるようにしています。また、このサンプルでは、オブジェクト インスペクタによる「イベントハンドラの共有」を行わず、コードで、その設定を 行っています。

procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.Caption := (Sender as TButton).Caption + 'が押されました';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button2.OnClick := Button1.OnClick;
end;

このサンプルでは「Button1, Button2 のどちらが押されたか」を識別しています。 正確には識別ではなく、押されたボタンの Caption を表示しているだけですが。

また、サンプルでは as 演算子を使用しています。as 演算子は型キャストを行いますが、 そのキャストが有効でなければ、例外を生成します。as 演算子以外にも、is 演算子というのもありますが、is 演算子の場合は、キャストされる対象が、指定された型に キャストできるかどうかを Boolean で返します。

もう少し複雑にして、例えば、押されたボタンに応じて、処理を分岐する場合には、 以下のようなコードになります。

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Sender = Button1 then
    Color := clRed
  else if Sender = Button2 then
    Color := clWhite;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button2.OnClick := Button1.OnClick;
end;

Button1 を押すとフォームの色を赤に、Button2 の場合には白に変更しています。

ただ、押されたボタンに応じて、処理を分岐するのであれば、 それぞれ別々のイベントハンドラとして定義する方が望ましいです。

まとめ


up
Last update: 2003/4/8