†Sibylのお部屋†
作成開始日 2022.03.02
最終更新日 2022.03.03
Var miMyItem : Array[1..10] of TMenuItem; Begin //オブジェクトの生成 For n:=1 to 10 Do Begin miMyItem[n]:=TMenuItem.Create(Self); End; //項目名の設定;設定文字列は任意 miMyItem[1].caption:='e:\data\abc.txt'; miMyItem[2].caption:='d:\bin\myrexx.cmd'; miMyItem[3].caption:='f:\img\myimg.jpg'; …キモは、メニュー項目はオブジェクト(つまりポインタ)なので、変数型を宣言しただけでは使用できず、Createでインスタンスを生成する必要がある点。オブジェクトの作成を一般化すると、次のようになる;
Var Object: TClass; Begin Object:=TClass.Create(Self);次に、生成したメニュー項目の名称を設定している。設定内容は処理によって様々だが、ここでは例としてファイルのフルパス名を指定しておいた。尤も、メニュー項目を動的に生成する場合は、設定ファイルやキー入力で内容を指定する可能性が高く、ここで示しているように、直接ソースに設定を書く事はないと思うが(フォームデザイン段階で確定出来るので、動的にする生成する意味がない)。
For n:=1 to 10 Do Begin PopupMenu1.items.Add(miMyItem[n]); //itemsがキモ End;これで、PopupMenu1.Popupを実行すれば、生成された項目が表示されるようになる。キモはTPopupMenu型の「items」プロパティで、これがメニューツリーのルートに相当する。つまり、この作業は、メニューのルートに項目を追加していることになる。もし、サブメニューに追加したければ、親となるメニュー項目にAddする。例えば、MainMenu1にMenuItem3という項目が存在し、MenuItem3のサブメニューに生成した項目を追加する場合には;
For n:=1 to 10 Do Begin MainMenu1.MenuItem3.Add(miMyItem[n]); End;
加えて、動的生成は数も内容も事前に確定していないのが一般的なので、事前に各項目毎にイベントハンドラを定義しておくことは難しい。そうではなく、動的生成した同一グループの項目に関しては、単一のイベントハンドラを起動して、ハンドラ内で分岐処理をする方が賢明。たとえば、「MyMenuOnClick」というプロシージャを定義しておいて;
Private Procedure MyMenuOnClick(Sender: TObject); …… Begin //OnClickイベントハンドラの登録 For n:=1 to 10 Do Begin miMyItem[n].OnClick:=MyMenuOnClick; End;のような要領で10個の項目全てに同一のOnClickイベントハンドラを指定しておく。イベントハンドラが単純なプロパティのような形式で設定できるのがミソ。
さて、問題はMyMenuOnClickの中身;処理内容はともかく、どうやってメニュー項目を識別して処理を分岐させるか?基本的には、送り元の項目である「Sender」をチェックすれば判別可能。
Procedure TForm1.MyOnClick(Sender: TObject); Begin If Sender=miMyItem[1] then .... If Sender=miMyItem[2] then .... If Sender=miMyItem[3] then ....まあ、この形式が基本なんだが、これは余りにカッコ悪い。それに、メニュー項目で処理に使用するプロパティはたいてい「Caption」だろう。前述の「最近開いたファイル」も、項目名にファイルのパスが含まれているので、「Caption」情報だけ取得できれば、後の情報はほぼ不要である。ならば、Senderから直接Captionを取得するとか、SenderをTMenuItem型オブジェクトとして扱うとかはできないだろうか?
Procedure TForm1.MyOnClick(Sender: TObject); Var mi: TMenuItem; st: string; Begin st:=Sender.caption; //これは無理… mi:=Sender; //これも無理…これができれば便利なのだが、残念ながら、このままではこれは不可能。そりゃそうだ、TObjectがTMenuItemのプロパティを全て持っているわけではない。しかし、クラス型のキャストを行えば、代入が可能になる。
Procedure TForm1.MyOnClick(Sender: TObject); Var mi: TMenuItem; st: string; Begin mi:=Sender as TMenuItem; //クラス型キャスト st:=mi.caption; //呼び出し元の項目の項目名を取得これによって、何番目の項目かを考える事なく、選択した項目の項目名が取得できる。また、これはオブジェクトはポインタなので、参照だけではなく代入も反映される。つまり;
mi.caption:='abc...';のような処理を行えば、呼び出し元のmiMyItem[n]のCaptionを変更することができる。