CheckBoxの排他的選択の後続

解決


イルカ  2007-11-07 23:53:09  No: 28271

前回「解決」した内容ですが,1つ不明なところがありましてもう1回書き込みます.

前回の内容:
https://www.petitmonte.com/bbs/answers?question_id=5187

前回,CheckBox1.OnClick := CheckBoxClickについて,
「これは、チェックのON/OFFが切り替わったときに、CheckBoxClickを
実行するようにしましょうと、定義しているだけで、
CheckBoxClickを
実行しているわけではない。」と教えていただきましたが,
結局はCheckBoxClick関数の中身を実行してるんじゃないでしょうか.
その違いがよく分かりません.

また,CheckBoxClickの宣言をCheckBoxClick(Sender: TObject)ではなく,
CheckBoxClick(Sender: TObject; i: Integer);にしたいですが,無理でしょうか.
なぜか上記のようにすると,実行時エラーが出ます.


うんと  2007-11-08 00:12:48  No: 28272

イベントとは、メソッドポインタを一つだけ設定できるプロパティーなのです。

>CheckBox1.OnClick := CheckBoxClick

だから、これは、CheckBox1.OnClick というプロパティーに CheckBoxClick という
値を代入しているだけです。実際に CheckBox1 がクリックされたら CheckBoxClick を
呼んでね、ってことです。

で、プロパティーには型があります。TCheckBox.Click の型はヘルプを見ると
property OnClick: TNotifyEvent; ですよね。

type TNotifyEvent = procedure (Sender: TObject) of object;

ですから、この型以外の CheckBoxClick(Sender: TObject; i: Integer); な形の
イベントハンドラは型が違うので代入できません。

もし代入できたとしても、誰がそれを呼ぶんですか? 

i: Integer

こんな変数に誰が代入してくれるんですか? イベントハンドラは、出来事が
起こったときに自動的に呼び出されます。普通はコードで呼び出すことは
ありません。(通常のメソッドですから、もちろんコードで呼び出すことも
できますが。)


じゃ  2007-11-08 01:22:28  No: 28273

イカで、Button1をクリックしたとき、メッセージが出たら、あなたの正解
出なければ不正解、ということでFA

procedure TForm1.Button1Click(Sender: TObject);
begin
  CheckBox1.OnClick := CheckBoxClick;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  CheckBox1.OnClick := nil;
end;

procedure TForm1.CheckBoxClick(Sender: TObject);
begin
  ShowMessage('CheckBoxClick');
end;


イルカ  2007-11-08 02:33:55  No: 28274

うんとさん
ご説明ありがとうございます.よく分かりました.

今やりたいのは,例えば,CheckBoxが40個あって,
5個ずつ1つのグループ,計8つのグループのCheckBoxを操作することです.
で,
for i := 1 to 5 do
  TCheckBox(FindComponent('CheckBox' + IntToStr(1) + '_' + IntToStr(i))).OnClick := CheckBoxClick1;
で1個目のCheckBoxグループの処理を;

for i := 6 to 10 do
  TCheckBox(FindComponent('CheckBox' + IntToStr(1) + '_' + IntToStr(i))).OnClick := CheckBoxClick2;
で1個目のCheckBoxグループの処理を...
させたいです.
それで,もし,CheckBoxClick(Sender: TObject; i: Integer);が使えれば,
iを変化させ,CheckBox1〜5の時はCheckBoxClick1を;
CheckBox6〜10の時は,CheckBoxClick2を...実行したら良いなと思ったわけです.


うんと  2007-11-08 02:53:39  No: 28275

これはFAQの一つです。
共通のイベントハンドラを割り当てたとき、どのコンポがイベントを起こしたのか
を知りたいのでしょう?

一番簡単なのは、Tag プロパティーを使う方法です。
新規で Form1 に CheckBox1-3 を置いてください。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    CheckBox3: TCheckBox;
    procedure FormCreate(Sender: TObject);
  private
    procedure KyotuuCheckBoxClick(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
  cb: TCheckBox;
begin
  for i := 1 to 3 do
  begin
    cb := TCheckBox(FindComponent('CheckBox' + IntToStr(i)));
    cb.OnClick := KyotuuCheckBoxClick;
    cb.Tag := i;
  end;
end;

procedure TForm1.KyotuuCheckBoxClick(Sender: TObject);
var
  no: integer;
begin
  no := TCheckBox(Sender).Tag;
  Showmessage('CheckBox'+ IntToStr(no) + ' Clicked!');
end;

end.

どれかのCheckBoxをクリックするたびのメッセージが表示されます。


HOta  2007-11-08 03:38:31  No: 28276

TRadioGroupを使うのは駄目なのでしょうか?


イルカ  2007-11-08 03:59:05  No: 28277

どのコンポがイベントを起こしたのだけなら,
CheckBox1〜3までの.CheckedがTureかどうか調べれば良いので簡単ですが,
やりたいのは,5個ずつ1つのグループの40個のCheckBoxを,
グループ毎に排他的選択をさせたいです.

TRadioGroupを使ったら一番簡単ですが,
どうしてもCheckBoxを使って実現したいと思いまして...


うんと  2007-11-08 04:14:38  No: 28278

>CheckBox1〜3までの.CheckedがTureかどうか調べれば良いので簡単ですが,

そんなことはありません。true/false はクリックするたびに入れ替わりますから。

>グループ毎に排他的選択をさせたいです.

ですから、Tag に1−40の番号を付けておけば、今クリックされたのが何番で
どのグループに属するかが分かると思いますけど。なにが問題なのですか?


イルカ  2007-11-08 05:00:55  No: 28279

うんとさん

ご指摘の通りですが,Tagを使う方法だと,
CheckBoxの排他的処理が面倒になるような気がします.
下記のページ
https://www.petitmonte.com/bbs/answers?question_id=5187
にある,
  TCheckBox(FindComponent('CheckBox' + IntToStr(i))).OnClick := nil;
  if Sender <> TCheckBox(FindComponent('CheckBox' + IntToStr(i))) then
    TCheckBox(FindComponent('CheckBox' + IntToStr(i))).Checked := False;
    TCheckBox(FindComponent('CheckBox' + IntToStr(i))).OnClick := ClickCheckBox;
よりは.


うんと  2007-11-08 05:13:58  No: 28280

CheckBox1−15  で5個ずつ3グループの例を示します。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    CheckBox3: TCheckBox;
    CheckBox4: TCheckBox;
    CheckBox5: TCheckBox;
    CheckBox6: TCheckBox;
    CheckBox7: TCheckBox;
    CheckBox8: TCheckBox;
    CheckBox9: TCheckBox;
    CheckBox10: TCheckBox;
    CheckBox11: TCheckBox;
    CheckBox12: TCheckBox;
    CheckBox13: TCheckBox;
    CheckBox14: TCheckBox;
    CheckBox15: TCheckBox;
    procedure FormCreate(Sender: TObject);
  private
    cb: array[1..15] of TCheckBox;
    procedure CBClick(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
  ccb: TCheckBox;
begin
  for i := 1 to 15 do
  begin
    ccb := TCheckBox(FindComponent('CheckBox'+IntToStr(i)));
    ccb.OnClick := CBClick;
    ccb.Tag := i;
    cb[i] := ccb;
  end;
end;

procedure TForm1.CBClick(Sender: TObject);
var
  i, j, t: integer;
begin
  t := TCheckBox(Sender).Tag;
  j := ((t - 1) div 5) * 5 + 1;
  for i := j to j+4 do
  begin
    cb[i].OnClick := nil;
    cb[i].Checked := false;
  end;
  cb[t].Checked := true;
  for i := j to j+4 do
    cb[i].OnClick := CBClick;
end;

end.

FindComponent を毎回使うのは非効率ですから、一度だけ配列にとって
おきます。40個8グループに拡張してみてください。


イルカ  2007-11-08 08:51:15  No: 28281

うんとさん

さすがですね!感心しました!
Tag プロパティーを利用すると本当に簡単ですし,すっきりしますね.
勉強になりました.解決です!


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

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






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