破棄されたフォームを知るには?

解決


ahsan  2008-09-19 23:26:06  No: 32012

以下のような関数:checkCreatedFormを作成して
チェックしました。
あるフォームを
FormA:=TFormA.Create(Application);
FormA.Show;
として、FormA.FormCloseで
Action:=caFree;
とした後、
res:=checkCreatedForm(FormA);
を呼び出すと、resにTrueを返します。
フォーム生きているか、破棄されたかどうかを
どうやって知ればよいのでしょうか?

function checkCreatedForm(fm:TForm):boolean;
var
  i  : integer;
begin
  Result:=False;
  for i:=0 to Screen.FormCount-1 do begin
    if Screen.Forms[i] = fm then begin
      Result:=True;
      break;
    end;
  end;
end;


Basser  2008-09-19 23:48:16  No: 32013

FormA破棄時に通知するように設定します。
下記を参考にしてみてください。

  type
    TForm1 = class(TForm)
      Button1: TButton;
      procedure Button1Click(Sender: TObject);
    private
      FChildFrm: TForm;
    protected
      procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    public
    end;

  var
    Form1: TForm1;

  implementation

  uses Unit2;

  {$R *.DFM}

  procedure TForm1.Button1Click(Sender: TObject);
  begin
    if FChildFrm = nil then
    begin
      FChildFrm:= TForm2.Create(Self);
      FChildFrm.FreeNotification(Self);
      FChildFrm.Show;
    end;
  end;

  procedure TForm1.Notification(AComponent: TComponent; Operation: TOperation);
  begin
    inherited Notification(AComponent, Operation);

    if (AComponent = FChildFrm) and (Operation  = opRemove) then FChildFrm:= nil;
  end;


ahsan  2008-09-20 00:30:00  No: 32014

Basserさん、早速ありがとうございます。
参考にさせていただき、.Notificationにて、FormAにnilを代入すると
res:=checkCreatedForm(FormA);
でFalseを返すようになりました。
ありがとうございました。

実は、FormA.FormCloseで
Action:=caFree;
とした後、もうひとつFormBをCreateしShow
した後にTrueを返すような現象が発生しました。
(FormBのCreate,Showをしなければ正しくFalseを返します)
調べてみましたところ、
FormA.NameとFormB.Nameを監視式に追加し、
FormB:=TFormB.Create(Application);
を実行した直後ブレイクポイントをかけて見ると、
FormB.Nameは'FormB'でいいのですが、
FormA.Nameも'FormB'となります。
よって、関数内のif文
if Screen.Forms[i] = fm then begin
で条件が一致しTrueになっています。
なぜFormAにもFormBが入ってしまうのか
分からないのです。
DelphiはDelphi2007 Pro です。


  2008-09-20 02:57:23  No: 32015

ソースプリーズ

> FormB:=TFormB.Create(Application);
これの前後に FormA.Close しているか書いてないし、情報不足。

FormA,FormB どっちも自動起動 off になってる?


うんと  2008-09-20 06:50:02  No: 32016

TFormA も TFormB も TForm の派生クラスだから

> if Screen.Forms[i] = fm then begin

で fm が TForm なら区別できないのは当たり前です。


うんと  2008-09-20 06:57:16  No: 32017

あー、言いたいのは

function checkCreatedForm(fm:TForm):boolean;

はまったく無力。Basser さんのように TFormA も TFormB もインスタンスを一つに
限定したいのなら

    private
      FChildFrmA: TFormA;
      FChildFrmB: TFormB;  
    protected

のようにして、

If Assigend(FChildFrmA) then

で確認するとよいです。


KHE00221  2008-09-20 10:42:09  No: 32018

これ正常に動作するだろ?

function checkCreatedForm(fm:TForm):boolean;
var
  i  : integer;
begin
  Result:=False;
  for i:=0 to Screen.FormCount-1 do begin
    if Screen.Forms[i] = fm then begin
      Result:=True;
      break;
    end;
  end;
end;

>として、FormA.FormCloseで
>Action:=caFree;
>とした後、
>res:=checkCreatedForm(FormA);
>を呼び出すと、resにTrueを返します。

FormA.FormClose 内で  res:=checkCreatedForm(FormA); を実行して
Trueを返すと言ってる気がするんだけど・・・


うんと  2008-09-20 12:15:48  No: 32019

KHE00221 さん

>これ正常に動作するだろ?

ああああ、そうか! 失礼!  前言全面撤回します。

うーん、

>FormA.FormClose 内で  res:=checkCreatedForm(FormA); を実行して
>Trueを返すと言ってる気がするんだけど・・・

だとすると、質問者がなにか勘違いしてますね。FormA.FormClose 内で実行するのは
ナンセンスですものね・・


ofZ  2008-09-20 21:52:03  No: 32020

ついでに、FormAは、クラスがTFormAでよいとして、FormBが別のクラス
TFormBなのかTFormAの別の変数なのかわからんな〜
そんなわけで、我はソースを要求する。


ahsan  2008-09-23 20:38:07  No: 32021

遅くなり、みなさんすみません。
・ソース以下3つ(unitMain,unitA,unitB)です。
・FormA,FormBは自動生成フォームから外しています。
・ButnAをクリックして、FormAを表示→閉じるし、
  ButnChkをクリックするとFalseと表示します。
・ButnAをクリックして、FormAを表示→閉じるし、
  ButnBをクリックして、FormBを表示し、
  ButnChkをクリックするとTrueと表示します。

unit unitMain;

interface

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

type
  TFormMain = class(TForm)
    ButnA: TButton;
    ButnB: TButton;
    ButnChk: TButton;
    procedure ButnAClick(Sender: TObject);
    procedure ButnBClick(Sender: TObject);
    procedure ButnChkClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation
uses
  unitA,unitB;

{$R *.dfm}

function checkCreatedForm(fm:TForm):boolean;
var
  i : integer;
begin
  Result:=False;
  for i:=0 to Screen.FormCount-1 do begin
    if Screen.Forms[i] = fm then begin
      Result:=True;
      break;
    end;
  end;
end;

procedure TFormMain.ButnAClick(Sender: TObject);
begin
  FormA:=TFormA.Create(Application);
  FormA.Show;
end;

procedure TFormMain.ButnBClick(Sender: TObject);
begin
  FormB:=TFormB.Create(Application);
  FormB.Show;
end;

procedure TFormMain.ButnChkClick(Sender: TObject);
begin
  if checkCreatedForm(FormA) = True then ShowMessage('True')
  else                                   ShowMessage('False');
end;

end.

unit unitA;

interface

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

type
  TFormA = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormA: TFormA;

implementation

{$R *.dfm}

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

end.

unit unitB;

interface

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

type
  TFormB = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormB: TFormB;

implementation

{$R *.dfm}

end.


ttt  2008-09-23 21:54:54  No: 32022

FormAとFormBの指す値を表示してみるとわかりますが、
一度一方のFormを破棄してからFormを再作成すると同じ値になります。
つまりメモリ上の同じ領域が再利用されているわけですね。
変数FormAの値自体はクリアしていないので、チェックした時点で
FormA=FormB となっている。これがTrueになる理由です。


KHE00221  2008-09-23 22:06:11  No: 32023

FormA を作成 (アドレス 10000)
FormB を作成 (アドレス 11000)
として両方Free後 
FormB を作成すると アドレス (10000) に作成されてしまい 

FormA と FormB が同じ アドレス になってしまうので

>なぜFormAにもFormBが入ってしまうのか
>分からないのです。

みないな事が起きます。

(*) アドレスは適当

Notificationでもいいですが

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

のように  FormClose で自分自身を指す変数に nil を代入でも平気です

こんな感じにしてアドレスを見ればわかると思います。

procedure TForm1.Button1Click(Sender: TObject);
begin
    Memo1.Lines.Clear;

    Memo1.Lines.Add(IntToStr(Screen.FormCount));
    if CheckCreatedForm(Form2) = True then Memo1.Lines.Add ('Form2Createed') else Memo1.Lines.Add ('Form2Free');

    Memo1.Lines.Add(IntToStr(Integer(Form2)));

    Memo1.Lines.Add(IntToStr(Screen.FormCount));
    if CheckCreatedForm(Form3) = True then Memo1.Lines.Add ('Form3Createed') else Memo1.Lines.Add ('Form3Free');

    Memo1.Lines.Add(IntToStr(Integer(Form3)));

end;

また、名前で判断させれば Free にさせなくても出来ます

function checkCreatedForm2(fm:String):boolean;
var
  i  : integer;
begin
  Result:=False;
  for i:=0 to Screen.FormCount-1 do
  begin
    if Screen.Forms[i].Name = fm then
    begin
      Result:=True;
      break;
    end;
  end;
end;

if Assigned(Form3) = True then
begin
  if CheckCreatedForm2(Form3.Name) = True then Memo1.Lines.Add ('Form3Createed') else Memo1.Lines.Add ('Form3Free');
end
else
begin
  Memo1.Lines.Add ('Form3Free');
end;


ahsan  2008-09-24 03:32:00  No: 32024

tttさん、ありがとうございます。
>一度一方のFormを破棄してからFormを再作成すると同じ値になります。
異なるユニットの異なる変数なのに、そうなってしまうのですね。

KHE00221さん、ありがとうございます。
>のように  FormClose で自分自身を指す変数に nil を代入でも平気です
それは知りませんでした。
自身をFormCloseしてる最中に、nilを代入すると、
「私は誰?」になってしまわないのですね。

>こんな感じにしてアドレスを見ればわかると思います。
なるほど、同じ値をさしています。

>また、名前で判断させれば Free にさせなくても出来ます
Nameにすれば、不変になるんですね。
これで判断してみます。

>if Assigned(Form3) = True then
これは何をしているのですか?


・・・  2008-09-24 07:16:15  No: 32025

> >if Assigned(Form3) = True then
> これは何をしているのですか?
Help読みましょう


ahsan  2008-09-24 23:33:31  No: 32026

・・・さん
>Help読みましょう
すみません。
ヘルプで理解できました。
みなさんありがとうござました。


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

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






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