モードレスなフォーム

前回、モーダルなフォームについてやりました。今回はモードレスなフォームについてやってみましょう。今回も同様に、メインフォーム( Form1 )、サブフォーム( Form2 )としてやってみます。

「ファイル」→「新規作成」→「フォーム」で新たにフォームを追加してください。まず最初にサブフォームが「自動作成の対象」の状態のままでやってみます。Form1、Form2 それぞれに1つずつボタンを貼り付けて下さい。そして、Form1 の方でボタンを押すと Form2 を表示させるように以下のコードを書きます。

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.Show; // モードレスで表示
end;

特に問題は無いと思います。Form2 は「自動作成の対象」、つまりプログラムの実行直後に生成され、終了とともに破棄されます。(もちろん Delphi 自身が管理しています) 結局、上の例では、単に Show で Form2 を表示して、Close で非表示にしているだけということになります。つまり、Close で Form2 を閉じたとしても、それは単に見えなくしただけで、依然 Form2 の為のメモリは確保されたままということになります。

それでは、モーダル同様、モーダレスでも動的に生成、破棄するようにしてみましょう。
まず、何よりも忘れちゃいけないのは、このサブフォーム( Form2 )を「選択可能なフォーム」に設定することです。そして、プログラマが責任を持って、生成と破棄をしなければなりません。

モードレスとモーダルの違いは、それぞれフォームの切り替えが出来るか出来ないかです。モーダルの場合、そのモーダルフォームを閉じないと、別のフォームに移れませんので、簡単に処理することが出来ましたが、しかしモードレスの場合は、フォームの切り替えがいつでも出来ますので、「いつモードレスフォームが閉じられたのか」、言い換えますと、いつメモリを開放すればよいのかが問題となってきます。

「なんだ、そんなのモードレスフォームの OnClose イベントハンドラに書いてやればいいじゃないか」と思うかも知れません。しかし、実は重要なのは、そのフォームが閉じられる時ではなく表示(生成)する時なんです。例えば、メインフォームのボタンを押すと、モードレスフォームが生成され、そして表示されるようにしたとします。この場合、問題になってくるのが、このモードレスフォームを表示している状態で、再びメインフォームのボタンを押した場合にモードレスフォームが新たに生成されてしまうことにあります。そうなると、モードレスフォームが複数存在することになりますので、問題が生じてきます。

そこで、そのボタンを押した時にモードレスフォームが既に生成されているかどうかの判定が必要になってきます。それでは、一旦ここで、プログラムで表してみることにします。
Form1 にボタンを一つ、そして、Form2 を「選択可能なフォーム」に設定してください。 まず、メインフォーム( Form1 )の方で、モードレスフォーム( Form2 )を生成、表示するようにしてやります。

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Form2がまだ存在していなければ then
    Form2 := TForm2.Create(Application); // 生成
  
  Form2.Show; // 表示
end;

そして、モードレスフォーム側で、自分( Form2 )が閉じられた時にメモリの開放をしてやります。それには、OnClose イベントハンドラを使用します。

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

このように、Action := caFree; としてやりますと、このフォーム( Form2 )が閉じられた時にメモリを開放することが出来ます。

さて、ここで「モーダルフォームが既に存在するかどうか」というのが問題となってきますが、ここでは Assigned を使ってやれば解決できます。

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Form2 のインスタンスが存在するかどうか
  if not Assigned(Form2) then
    Form2 := TForm2.Create(Application);
 
  Form2.Show;
end;

このようにしてやりますと、モードレスフォーム、つまり Form2が存在しているならば if 文は実行されず Form2.Show;だけが実行されます。

これで、やっと終わりました。といきたいのですが、まだ大事な問題が残っています。 実は、Assigned では何が行われているのかというと、「変数 Form2が nil かどうか」ということを行っています。Assinged は nil ならば False、それ以外なら True を返します。(その為に、上の if 文では not を使っているわけです) そして、Form2 で行う Action := caFree; では、メモリの開放は行いますが、変数 Form2 を自動的に nil にはしてくれません。(この意味に関しては、「そういうものなんだな」ぐらいに思っていて下さい。)

その為、プログラマ自ら 変数 Form2 に nil を代入しなくてはなりません。結果、以下のようになります。

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  
  Form2 := nil;
end;    

これで、Assigned で適切な処理が行えるようになります。もし、nil を代入しなかった場合は、モードレスフォームを開放した後も Form2 は nil にはなりませんので、その状態でボタンを押すと、新しくモードレスフォームが生成されないまま、Form2.Show が実行されてしまいます。

また別な方法として、モードレスフォームが生成されたら、一度ボタンを使用不可にしておいて、そのモードレスフォームが破棄されたら再び使用可能にしてやることも一つの手ではあります。

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2 := TForm2.Create(Application);
  Form2.Show;
  
  Button1.Enabled := False; // 一旦、使用不可に
end;
 
  ..................................................
  
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Form1.Button1.Enabled := True; // 再び使用可能に
end;

up next
Last update: 2002/5/15