SpinEditのUpClickとDownClick

解決


yTake  2013-11-07 12:36:27  No: 45513

yTakeです。

基本的なことなのかもしれませんが、教えてください。

SpinEditと言うコンポーネントがあります。"Edit"と"UpDown"を足した様な物と思います。
DELPHIのヘルプによりますと、"UpClick", "DownClick"と言うメソッドがある様ですが、DELPHI上でオブジェクトインスペクタのイベントタグ内に見つかりません。何かリストされるメソッドに設定があるのでしょうか?
上下矢印キーをクリックした際に、Valueを増減すると共にそれに応じた別の処理をさせたいと考えています。

よろしくお願いします。


通りすがり  2013-11-08 00:36:32  No: 45514

メソッド≠イベント


yTake  2013-11-08 01:44:50  No: 45515

通りすがりさん

ご指摘ありがとうございます。

不正確な用語を用いてしまって申し訳ありません。

なるほど、先入観を除外して読めば、分かってきました。
何より、種類でproperty, event, procedure等は明示されている分けで、、、

思い込みによる使用法の誤りでした。

結論として、SpinEditは"onClick"のイベント内でその引数にButtonがありません。つまり、SpinEditはコンポーネントとしてはいずれの矢印が押されたかは知りえないと言う事でしょうか?(onChangeでも変化前の値が分からないと増加したのか減少したのかの判断も出来かねます)
一方で、"UpDown"コンポーネントは"onClick"イベント内で押されたButtonを特定できそうです。そこで"Edit"とAssociateさせる事で"SpinEdit"的に使う方法が良いのではと考えています。

もし、うまく行かなければ、質問させて頂くかも知れません。

その際は宜しくお願いします。

ありがとうございました。


Harry  2013-11-08 03:30:12  No: 45516

設定はありません。メソッドじゃなくて、イベントプロパティですね。
publishedなpropertyのうち、メソッドポインタのものがイベントプロパティで、イベントページタブにリストされます。

>オブジェクトインスペクタは,プロパティの型によってそのプロパティがイベントかどうかを判断します。
>すべてのメソッドポインタプロパティはイベントとみなされ,[イベント]ページに表示されます。

Win32 開発者ガイド - コンポーネント開発者ガイド - イベントの作成 - イベントの宣言
http://docwiki.embarcadero.com/RADStudio/XE5/ja/%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E5%AE%A3%E8%A8%80

Delphi 言語ガイド - クラスとオブジェクト - イベント(Delphi)
http://docwiki.embarcadero.com/RADStudio/XE5/ja/%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88

>SpinEditはコンポーネントとしてはいずれの矢印が押されたかは知りえないと言う事でしょうか?
知ってるけど、SpinEditはサンプル扱いなので(学習のため?)普通に必要とするイベントをあえて省いて
あるんじゃないでしょうか。なので、自分で必要な機能を追加して使えばよいと思います。

以下は機能を追加する、いくつかの方法です。
(a) TSpinEdit内部のTSpinButton(SpinEdit1.Button)のイベントに、「別の処理」を寄生させる
(b) TSpinEditを継承してUpClickとDownClickをoverrideし、OnUpClickとOnDownClickイベントを実装する
(c) クラスヘルパーを使いTSpinEditにOnUpClickとOnDownClickイベントを追加する(←知ったかぶりです。出来るのか知りません。)

>onChangeでも変化前の値が分からないと増加したのか減少したのかの判断も出来かねます
ならば、前の値を保存しておけば出来ますね。場合によってはそういう方法もアリかと。


Harry  2013-11-08 03:38:50  No: 45517

(a)のコード例

type
  TForm1 = class(TForm)
    SpinEdit1: TSpinEdit;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    FDefOnUpClick: TNotifyEvent;
    FDefOnDownClick: TNotifyEvent;
    procedure SpinUpClick(Sender: TObject);
    procedure SpinDownClick(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDefOnUpClick:=SpinEdit1.Button.OnUpClick;
  SpinEdit1.Button.OnUpClick:=SpinUpClick;

  FDefOnDownClick:=SpinEdit1.Button.OnDownClick;
  SpinEdit1.Button.OnDownClick:=SpinDownClick;
end;

procedure TForm1.SpinUpClick(Sender: TObject);
begin
  FDefOnUpClick(Sender);

  Memo1.Lines.Add('Up!');
end;

procedure TForm1.SpinDownClick(Sender: TObject);
begin
  FDefOnDownClick(Sender);

  Memo1.Lines.Add('Down!');
end;

------------------------------------------------------------------------------------------
(b)のコード例

type
  TSpinEdit = class(Spin.TSpinEdit)
  protected
    procedure UpClick (Sender: TObject); override;
    procedure DownClick (Sender: TObject); override;
  private
    FOnUpClick: TNotifyEvent;
    FOnDownClick: TNotifyEvent;
  end;

type
  TForm2 = class(TForm)
    SpinEdit1: TSpinEdit;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    procedure SpinUpClick(Sender: TObject);
    procedure SpinDownClick(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TSpinEdit.UpClick(Sender: TObject);
begin
  inherited;
  if Assigned(FOnUpClick) then FOnUpClick(Sender);
end;

procedure TSpinEdit.DownClick(Sender: TObject);
begin
  inherited;
  if Assigned(FOnDownClick) then FOnDownClick(Sender);
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  SpinEdit1.FOnUpClick:=SpinUpClick;
  SpinEdit1.FOnDownClick:=SpinDownClick;
end;

procedure TForm2.SpinUpClick(Sender: TObject);
begin
  Memo1.Lines.Add('Up!');
end;

procedure TForm2.SpinDownClick(Sender: TObject);
begin
  Memo1.Lines.Add('Down!');
end;


Harry  2013-11-08 03:46:38  No: 45518

もう一つおまけで…
(b)の継承するコードにプロパティをちゃんと実装し、外部のSpinExユニットとして分離

usesにSpinExを追加する。

type
  TForm3 = class(TForm)
    SpinEdit1: TSpinEdit;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    procedure SpinUpClick(Sender: TObject);
    procedure SpinDownClick(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure TForm3.FormCreate(Sender: TObject);
begin
  SpinEdit1.OnUpClick:=SpinUpClick;
  SpinEdit1.OnDownClick:=SpinDownClick;
end;

procedure TForm3.SpinUpClick(Sender: TObject);
begin
  Memo1.Lines.Add('Up!');
end;

procedure TForm3.SpinDownClick(Sender: TObject);
begin
  Memo1.Lines.Add('Down!');
end;

------------------------------------------------------------------------------------------
// SpinEx.pasユニット

unit SpinEx;

interface

uses
  Classes, Spin;

type
  TSpinEdit = class(Spin.TSpinEdit)
  protected
    procedure UpClick (Sender: TObject); override;
    procedure DownClick (Sender: TObject); override;
  private
    FOnUpClick: TNotifyEvent;
    FOnDownClick: TNotifyEvent;
  public
    property OnUpClick: TNotifyEvent read FOnUpClick write FOnUpClick;
    property OnDownClick: TNotifyEvent read FOnDownClick write FOnDownClick;
  end;

implementation

procedure TSpinEdit.UpClick(Sender: TObject);
begin
  inherited;
  if Assigned(OnUpClick) then OnUpClick(Sender);
end;

procedure TSpinEdit.DownClick(Sender: TObject);
begin
  inherited;
  if Assigned(OnDownClick) then OnDownClick(Sender);
end;


yTake  2013-11-08 04:31:08  No: 45519

Harryさん、大変ありがとうございます。

”SpinEdit”って学習用でサンプルなのですか。

>あるんじゃないでしょうか。なので、自分で必要な機能を追加して使えばよいと思います。

>以下は機能を追加する、いくつかの方法です。
>(a) TSpinEdit内部のTSpinButton(SpinEdit1.Button)のイベントに、「別の処理」を寄生させる
>(b) TSpinEditを継承してUpClickとDownClickをoverrideし、OnUpClickとOnDownClickイベントを実装する
>(c) クラスヘルパーを使いTSpinEditにOnUpClickとOnDownClickイベントを追加する(←知ったかぶりです。出来るのか知りません。)

漠然と、必要な機能は使用者側で機能追加すれば良い、その為のオブジェクト指向でもあるわけですが、ご提示の(b)のやり方は考え付きましたが、敷居が高そうで、トライしていません。

また、変更前のValueを保持しておく手法もありと言う事ですが、この場合でも、まさしく、TSinEditを継承してproperty:last_Valueなどを追加した新たな子を定義すればもっともらしいコーディングになるのでしょうね。

示唆に富むリプライをありがとうございました。


yTake  2013-11-08 04:52:49  No: 45520

Harryさん

サンプルコードを拝見しています。

基本的なことですが、

type
  TSpinEdit = class(Spin.TSpinEdit)

は、継承した新たな派生クラスの定義と思います。
”=”の右辺が継承元クラスで、左辺が継承先の新しいクラス名称と理解していますが、本例では、新たな継承クラスの名称が継承元と同じ”TSpinEdit”となっています。継承元と同じ名称でも構わないのですね。
procedure等では、override指定による既存のprocedureを上書きする様な定義が出来ると解釈しています。同じ様な事と解釈して良いのでしょうね。


Harry  2013-11-08 09:14:02  No: 45521

>(b)のやり方は考え付きましたが、敷居が高そうで、トライしていません。
見ての通り簡単ですから、ジャンジャンやっちゃってください。
UpClickとDownClickは「overrideしてくれ」と言ってるように思えますので。

>”SpinEdit”って学習用でサンプルなのですか。
私はそう思ってます。少なくともオマケ扱いなのは確かです。(Delphi6 Personalではヘルプに載ってないので。)
正確なところはDelphiの歴史を知る方でないと分からないかと。

>変更前のValueを保持しておく手法もありと言う事ですが、
どうにもそれしか手がない場合は、ですかねー。出来る限り真っ当な方法を採用したいです。

>TSinEditを継承してproperty:last_Valueなどを追加した新たな子を定義すればもっともらしいコーディングになるのでしょうね。
ですです。(でも今回はLastValueという苦しい手を使う必要は無いですね。)
  private
    FLastValue: String;
  public
    property LastValue: String read FLastValue;
ただし、クラスの内部的に使うだけならpropertyを用意せず、FLastValueだけでいいです。
※クラス内の変数は「フィールド」と言います。ですから頭文字Fを付けるのが作法(命名規則)とされております。

>新たな継承クラスの名称が継承元と同じ”TSpinEdit”となっています。継承元と同じ名称でも構わないのですね。
えーと、やり方によっては大丈夫です。  TSpinEdit = class(TSpinEdit)  のようにすると、
  [エラー] Unit2.pas(12): 'TSpinEdit' 型の宣言が完了していません    のようなエラーが出ます。
これを防ぐためにユニット名Spinをくっつけてます。

私もこの継承方法を初めて見たときは「なんじゃこりゃ!?」と思いましたが、これは常套手段になってますね。
その目的は「継承元と同じクラス名にするため」もそうなのですが、すでにIDEで配置してあるコンポーネントを、
そのままの状態で機能アップさせることが出来るのがポイントです。
普通は  TSpinEditEx = class(TSpinEdit)  のようにするので、このTSpinEditExは動的に生成するか、または
ちゃんとしたコンポーネントに仕立ててコンポーネントパレットから貼り付けなくてはなりません。

>override指定による既存のprocedureを上書きする様な定義が出来ると解釈しています。
そうですね、上書きされます。ただしoverrideは単純に「上書き」という一言では表せません。

なお、おまけとして紹介したSpinExユニットは、一部変更しもう少し手を加えると、カスタムコンポーネント
としてコンポーネントパレットに登録することが出来ます。


yTake  2013-11-09 02:29:18  No: 45522

Harryさん。
ありがとうございます。

>UpClickとDownClickは「overrideしてくれ」と言ってるように思えますので。

ソースの行間を読むとそう言う事なのでしょうね。
そこまでの域にはまだまだ到底達しそうにありませんが。

>私もこの継承方法を初めて見たときは「なんじゃこりゃ!?」と思いましたが、これは常套手段になってますね。
その目的は「継承元と同じクラス名にするため」もそうなのですが、すでにIDEで配置してあるコンポーネントを、
そのままの状態で機能アップさせることが出来るのがポイントです。

経書元と同じクラス名にする事だけが目的であれば、逆に継承されたクラスである事が明示的に分かった方が良いのでは?と思いましたが、実はIDEに配置してある(=インストールしてある)コンポーネントをそのままの状態で機能アップする事の方が主目的なのですね。
これは重要な視点です。単にコンポーネントも自動的に継承されると思いがちです。

>普通は  TSpinEditEx = class(TSpinEdit)  のようにするので、このTSpinEditExは動的に生成するか、または
ちゃんとしたコンポーネントに仕立ててコンポーネントパレットから貼り付けなくてはなりません。

確かにこの方が素直に納得できますが、この継承クラスは実際に使用可能にするには、”TSpinEditEx”をコンポーネントとして登録し、パレットから貼り付ける必要があるのですね。こう言う事は指南されないと分からない事でしょう。

勉強になります。ありがとうございました。

トライしてみます。


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

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






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