色々なFormのCreateを動的に行うには

解決


もじゃもじゃ  2011-01-28 01:35:10  No: 39921

たびたびお世話になります。
Windows7、Delphi2007です。

子フォームを生成する場合、
Form2 := TForm2.Create(Self);
とすると思います。
この「Form2」「TForm2」の部分を動的に変えたいのです。

イメージとしては
yobidasi( TForm2 );
とか
yobidasi( TForm3 );
yobidasi( TForm4 );

のように引数を指定し呼び出して、

procedure yobidasi( param : ????? );
var
    frm : param;
begin
    frm := param.Create(Self);
    frm.Showmodal;
end;

てな感じで指定のフォームを生成して表示したいのです。
よろしくお願いします。


au  2011-01-28 02:21:02  No: 39922

procedure yobidasi( param : TFormClass );
var
    frm : TForm;
begin
    frm := param.Create(Self);
    frm.Showmodal;
end;

かな。「クラス参照」でヘルプを参照したら説明が載ってます。


もじゃもじゃ  2011-01-28 02:51:57  No: 39923

バッチリ表示できました。ありがとうございます。
ただ上記イメージにて割愛した部分にて不都合が出てしまいましたので
後出しになって申し訳ないですが引き続きお願いします。

実はForm2を呼び出す際、Form1から、Form2にあるpublic関数(kidou)を実行し、
kidouの中でForm2.Showmodalを行っています。

// 旧
Form2 := TForm2.Create(Self);
Form2.kidou;  // Form2のpublic関数 この中でForm2.Showmodal

// 新
procedure yobidasi( param : TFormClass );
var
    frm : TForm;
begin
    frm := param.Create(Self);
    frm.kidou;  // ←未定義の識別子に
end;

コンパイルが通らないことが感覚では理解できるのですが
どのようにすればよいでしょうか。
よろしくお願いします。


tor  2011-01-28 06:00:46  No: 39924

TForm2(frm).kidou のようにしてやれば実行はできますが
それではfrmが必ずTForm2(またはその派生クラス)でなくてはいけないから、動的生成する意味がないですよね。

実際にやりたいことは、TForm2, TForm3, TForm4, ... がすべて kidou というメソッドを持っていて、それを呼び出したいということだと思いますが
それを実現するには、まず kidou という仮想メソッドを持つ共通の親クラスを作らなくてはいけません。

type
 TForm_Parent = class(TForm)
 public
  procedure kidou; virtual; abstract;
 end;

その他のフォームはこのクラスから継承し、kidouメソッドをオーバーライドして実装します。

type
 TForm2 = class(TForm_Parent)
 public
  procedure kidou; override;
 end;

これで下のように動的にクラスを生成してメソッドを呼び出せます。

type
 TForm_ParentClass = class of TForm_Parent;

procedure yobidasi(param: TForm_ParentClass);
var
  frm: TForm_Parent;
begin
  frm := param.Create(Self);
  frm.kidou;
end;

ちなみに、やりたいことはそうじゃなくて「TForm2の時だけkidouを呼び出す」だというのなら、以下の一文で済みます。
if frm is TForm2 then TForm2(frm).kidou;


tor  2011-01-28 06:12:09  No: 39925

ついでに言うと、わざわざ親クラスを用意しなくても
ShowModal自体が仮想メソッドなので、これをオーバーライドしてしまうというのもアリです。

type
 TForm2 = class(TForm)
 public
  function ShowModal: Integer; override;
 end;

function TForm2.ShowModal: Integer;
begin
  // TForm2.kidou でやっていることをここに書く

  // その後で継承元のShowModalを呼び出す
  Result := inherited ShowModal;
end;

これなら呼び出す方はauさんのコードのままでOKです。

procedure yobidasi( param : TFormClass );
var
    frm : TForm;
begin
    frm := param.Create(Self);
    frm.Showmodal; // kidouでやっていたことをこの中でやってしまう
end;


もじゃもじゃ  2011-01-28 19:12:11  No: 39926

ご回答ありがとうございます。とてもわかりやすいです。

> 実際にやりたいことは、TForm2, TForm3, TForm4, ... がすべて kidou というメソッドを持っていて、それを呼び出したいということだと思いますが

そのとおりです。

ですがモノによっては、kidou関数は引数がついてたり返り値があったりなのでやっぱり無理か…
と挫折しかけましたが kidouのオーバーロードなり、別の関数なりを親クラスに定義しておけばできそうですね
そうすると呼び出し側で条件分岐しなければいけないので
動的に呼び出す意味がますます薄れるかと思いますが、ご容赦ください

さて、親クラスを継承する方法で進め、コンパイルはできました。
しかし実行時、Form2のkidou関数内ではForm2がnilになっており、エラーになってしまいます。
前段階のfrm変数は生成できているようなのですが…

// Form1---------------------------------------------------------
TForm_ParentClass = class of TForm_Parent;
TForm1 = class(TForm)

procedure TForm1.Button1Click(Sender: TObject);
begin
  yobidasi(TForm2);
end;

procedure TForm1.yobidasi(param: TForm_ParentClass);
var
  frm: TForm_Parent;
begin
  frm := param.Create(Self);
  frm.kidou;
end;

// Form2---------------------------------------------------------
  TForm2 = class(TForm_Parent)
  public
    { Public declarations }
    procedure kidou; override;
  end;

procedure TForm2.kidou;
begin
  inherited;
  Form2.ShowModal;  // ←ここでForm2がnil※※※※※※※※※※※※
end;

以上、よろしくお願いします。


tor  2011-01-28 19:28:13  No: 39927

> しかし実行時、Form2のkidou関数内ではForm2がnilになっており、エラーになってしまいます。

Form2というのはフォームを自動生成するときに使われる変数なので
自分で作った場合はそこには入りません。

そもそもTForm2のメソッドなのですから、自分自身を参照するのにわざわざ外部変数に頼る必要はなくて

  ShowModal;

だけでいいです。どうしても前に何か付いていないと不安だというなら

  Self.ShowModal;

とします。


もじゃもじゃ  2011-01-28 19:47:15  No: 39928

> だけでいいです。どうしても前に何か付いていないと不安だというなら
>   Self.ShowModal;
> とします。

不安です。笑
Self.ShowModalとしました。
完全に納得です。ありがとうございました。


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

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






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