コンポーネントのイベント実装


わっとっと  2013-07-23 20:23:11  No: 44908

こんにちは
奇妙な(あほうな)ことをおききすることになるかもしれませんが
どうしても理解できないことがあります。ご教授願いませんでしょうか?
現在カスタムコンポーネントを作っておりますがそれに対するイベントの
実装についてどうしても理解できないことがあります。誤りがあれば
つっこんでいただけませんでしょうか?

基本的な理解がおかしいのでしょうか?

あるコンポーネントにおいてメソッドが実行されたとき通常いくつかの
プロパティが変化する。それらひとつひとつがイベントである。それぞれの
イベントを発生させるメソッドは決まっていてあるイベントの動作を変更
させたいならそれを引き起こすメソッドをオーバーライドし、処理を変更
する。

イベントの考え方はこれでよろしいでしょうか?ならば

たとえばあるコンポーネントでOnKeyPressを定義(変更)したいなら
KeyPressをオーバーライドする(細かい処理は省略)
procedure KeyPress(Sender: TObject; var key char); pverride;
...
published
 property OnKeyPress;
...
procedure XXX.KeyPress(Sender: TObject; var key char);
begin
 inherited KeyPress(key); 
 showmessage(key);
end;
これでたとえばTEditのようなコントロールでキー入力をするたびに入力
された文字がメッセージボックスに出るはずですがこれはよいのです
(理解できる)が、TTreeViewでOnEditedイベントの処理を変更したくて
Editイベントをオーバーライドするような場合にはどうでしょう?
OnEditedの引数は(Sender: TObject;  Node: TTreeNode; var S:string)
ですがEditの引数は (const Item: TTVItem)です。引数が違いますよね。
これだと前出したOnKeyPressの例のような標準イベントが持つ引数を使う
処理ができないと思うのですがどうでしょう?こんな風にです
procedure Edit(const Item: TTVItem); override;
...
published
 property OnEdited;
...
procedure XXX.Edit(const Item: TTVItem);
begin
 inherited Edit(Item);
 showmessage(S) <---こういったことしたくてもできませんよね。
end;

私の考えでいくと、あるイベントとそれを引き起こすメソッドは1対1
(または多対1)で対応しており、それぞれの引数は一致していないと
おかしい(使えない)のではないかと思うのです。特にOnEditedとEdit
のように共通部分がまったくないのはどういうことか?と考えているの
ですが...おかしいでしょうか?

わっとっと


take  2013-07-23 21:00:48  No: 44909

複数の知りたい事を同時に聞かれても答えるのに苦労します。

Delphiは命名規則がしっかしている言語ですので
質問内容の部分から回答すると

Edit:メソッド(関数)
OnEdited:イベント

メソッドとイベントには何の関連もありませんよ
イベントを発生させるだけのメソッドであれば

OnをDoに変えたものを使用します。
OnEditedを発生させるだけのメソッドはDoEditedになります。

EditとOnEditedは無関係なので引数は一致しません。

Edit内で何が起きているのかは
ComCtrlsユニットの
procedure TCustomTreeView.Edit(const Item: TTVItem);
を参考にしてはいかがでしょうか?

具体的に何をしたいのかがわかれば力にもなれます


わっとっと  2013-07-24 02:12:50  No: 44910

質問が煩雑かつわかりにくくて申し訳ありません
やりたいこととわからないことははこういうことです
「既存のコンポーネントを改良したコンポーネントを作りたい。」
「イベントハンドラは私の場合どういうアプリケーションを作るときでも
  たいてい決まっているのでアプリケーション本体には書かず、処理を
  コンポーネントのユニットに書くようにしたい。そうすればどのような
  アプリケーションを作るときでもusesするだけで済むので自分としては
  RADだと考えた。」
「しかしその際にイベントを実装するやり方でつまずいた。」というわけ
です。改良コンポーネントMyEditを作ってみました。コードを見てください。
unit MyEdit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TREdit =class(TEdit)

   protected
     procedure DoEnter; override;  ...(1)
     procedure KeyPress(var Key: Char); override;  ...(2)
     procedure rClick(Sender: TObject);...(3)

   published
     property OnEnter;  ...(1)
     property OnKeyPress;  ...(2)
     property OnClick;  ...(3)

   public
     constructor CreateEX(Origin :TEdit);
   end;

implementation

constructor TREdit.CreateEX(Origin :TEdit);
begin
  OnClick := rClick;  ...(3)
end;

procedure TREdit.DoEnter;  ...(1)
begin
  inherited DoEnter;
  showmessage('Enter!');
end;

procedure TRedit.KeyPress(var Key: Char);  ...(2)
begin
  inherited KeyPress(Key);
  showmessage(key);
end;

procedure TREdit.rClick;  ...(3)
begin
  showmessage('Click!');
end;

end.
(1)(2)(3)と実装してみました(1)(2)は機能しますが(3)はエラーにはなり
ませんが実行しても機能しません(3)は直感的にはわかりやすいのですが
だめなのでしょうか。
また(2)はKeyPressがOnKeyPressを発生させているのではないのでしょうか。といった風に実装に関してわからないことが山積しております。

わっとっと


monaa  2013-07-24 02:45:49  No: 44911

クラスを学びたいのであればとりあえず基本的なTObjectから入ると楽ですよ。
Windowsコントロールを含むクラスはその後のほうが効率的だとおもいますよ。

Clickは
TControlのprotectedで
procedure Click; dynamic;
と定義されていますね。
継承するなら
procedure Click; ovrride;
ですよ。


take  2013-07-24 17:35:36  No: 44912

>イベントハンドラは私の場合どういうアプリケーションを作るときでも
>  たいてい決まっているのでアプリケーション本体には書かず、処理を
>  コンポーネントのユニットに書くようにしたい
  
なるほど、言いたい事はおよそ理解しました。
コンポーネントの設計としては正しい考えです。

そして様々なイベントを捕まえてコンポーネント側で
処理したいけど特にイベントが思うようにいかない
というわけですね。

>KeyPressがOnKeyPressを発生させているのではないのでしょうか

わからなければF7でトレースしていけばいいです。
標準ライブラリは普通トレースできませんが

プロジェクト→オプション→コンパイラ→デバッグ版DCUを使う
にチェックを付けてコンパイルする事で
どこまでもトレースする事が出来ます。
Standerd版は無理かも

クラス、特にオブジェクト指向は最初は大変かもしれませんが
一度理解してしまえば全ての言語で使える技術でもありますので
やって損は無いかと思います


にゃんこ  2013-07-31 11:08:25  No: 44913

constructor TREdit.CreateEX(Origin :TEdit);
begin
  OnClick := rClick;  ...(3)
end;

rClickは見たところ TNotifyEventのハンドラのようですが

CreateExのほかの部分はそのままに OnClickのイベントハンドラの指定のみを

procedure Afterconstruction; override;
あるいは
constructor Create(AOwner:TComponent); override
にうつしたらどうなりますかね・・・。

OnClickを指定しなくとも

monaa さんもおっしゃってますが

procedure Click; override;

procedure TREdit.Click;  
begin
  // inherited Click; 
  showmessage('Click!');
end;

とすると目的が達成できるように思います。

この場合、inherited Click をやらないと
property OnClick に Assignした TNotifyEventは呼ばれませんが
showmessageは実行されます。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加