Board index » delphi » Freeing Dynamically Created Forms

Freeing Dynamically Created Forms


2003-11-14 08:23:47 AM
delphi81
I've been unable to find an answer to this exact situation:
1. Form1 manages an array of Form2's
2. Form1 dynamically creates a Form2
3. The Form2 is shown non-modally (has to be non-modal)
4. User is done with the Form2, and closes it
5. How can Form1 tell this has happened?
Setting Action to caFree in Form2's OnClose doesn't set Form2 to NIL, which
I'd like to test for. I have tried various things from old postings.
Your help much appreciated. Using D3 here.
Pete
 
 

Re:Freeing Dynamically Created Forms

Pete <XXXX@XXXXX.COM>writes:
Quote
1. Form1 manages an array of Form2's
2. Form1 dynamically creates a Form2
3. The Form2 is shown non-modally (has to be non-modal)
4. User is done with the Form2, and closes it
5. How can Form1 tell this has happened?

Setting Action to caFree in Form2's OnClose doesn't set Form2 to NIL, which
I'd like to test for. I have tried various things from old postings.
Sorry, but there's no magic here. Somebody has to do something when an
instance of Form2 closes. Since only Form2 knows when it closes, that
means that either it has to tweak the array or it has to tell Form1 to
tweak the array. Note that updating the Form2 variable to nil would
not help - Form1 wouldn't know which Form2 has closed.
There are several different approaches to this. One is for Form1 to
give Form2 a pointer to the array element it needs to nuke when it
closes. Another is to give Form2 an event handler that it can invoke
when it closes, just like a TButton notifies the form when it is
clicked. If you need more details on this, please do follow up.
Good luck.
Kurt
 

Re:Freeing Dynamically Created Forms

On Fri, 14 Nov 2003 01:12:46 GMT, Kurt Barthelmess (TeamB) writes:
Quote
There are several different approaches to this. One is for Form1 to
give Form2 a pointer to the array element it needs to nuke when it
closes. Another is to give Form2 an event handler that it can invoke
when it closes, just like a TButton notifies the form when it is
clicked. If you need more details on this, please do follow up.

Good luck.

Kurt
Kurt, thanks for the great tips. Here's what seems to work for your first
approach:
For the main form (Form1):
procedure TForm1.Button1Click(Sender: TObject);
begin
if MyForms[3] = nil then begin
{$IFDEF Debug} ShowMessage('Creating'); {$ENDIF}
MyForms[3]:= TForm2.Create(Form1);
MyForms[3].Index:= 3;
end;
MyForms[3].Show;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Label1.Visible:= MyForms[3] = nil;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Label1.Caption:= 'NIL!!!';
end;
... and for the "child" form (Form2):
type
TForm2 = class(TForm)
procedure FormDestroy(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
Index : integer; // pointer into MyForms array
end;
var
MyForms : array [1..10] of Tform2;
implementation
{$R *.DFM}
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// force OnDestroy event
Action:= caFree;
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
// force array owner to NIL
MyForms[Index]:= nil;
end;
initialization
// all NILs
fillchar(MyForms, sizeof(MyForms), 0);
end.
 

Re:Freeing Dynamically Created Forms

A further simplification can be made:
Move the MyForms array into TForm1's Public declarations. This will get the
array initialized automatically at startup, so the init code in Form2's
unit can be deleted. Form2's OnDestroy handler then needs to be modified to
Form1.MyForms[Index]:= nil;
 

Re:Freeing Dynamically Created Forms

On Thu, 13 Nov 2003 16:23:47 -0800, Pete <XXXX@XXXXX.COM>writes:
Quote

I've been unable to find an answer to this exact situation:

1. Form1 manages an array of Form2's
2. Form1 dynamically creates a Form2
3. The Form2 is shown non-modally (has to be non-modal)
4. User is done with the Form2, and closes it
5. How can Form1 tell this has happened?

Setting Action to caFree in Form2's OnClose doesn't set Form2 to NIL, which
I'd like to test for. I have tried various things from old postings.
Does Form1 really need to hold an array of form references ?
An alternative approach could be something like this :-
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm2 = class(TForm)
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
ID: Integer;
public
{ Public declarations }
end;
function GetForm2( ID: Integer; CanCreate: Boolean ): TForm2;
implementation
{$R *.dfm}
function GetForm2( ID: Integer; CanCreate: Boolean ): TForm2;
var
i: Integer;
begin
Result := nil;
i := 0;
while not Assigned( Result ) and ( i < Screen.FormCount ) do
if ( Screen.Forms[i] is TForm2 ) and ( TForm2( Screen.Forms[i] ).ID = ID ) then
Result := TForm2( Screen.Forms[i] )
else
Inc( i );
if not Assigned( Result ) and CanCreate then
begin
Result := TForm2.Create( Application );
Result.ID := ID;
end;
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
end.
This would then allow you to write code like the following, without Form1 having to care
about whether a particular form reference was set to nil when an instance of the form is
closed.
procedure TForm1.Button1Click(Sender: TObject);
var
F: TForm2;
begin
F := GetForm2( 99, True );
F.Show;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
F: TForm2;
begin
F := GetForm2( 99, False );
if Assigned( F ) then
F.Caption := 'New Caption';
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
GetForm2( 99, False ).Close;
end;
John Leavey
 

Re:Freeing Dynamically Created Forms

On Fri, 14 Nov 2003 09:39:53 +0000, John Leavey <XXXX@XXXXX.COM>writes:
Quote
procedure TForm1.Button3Click(Sender: TObject);
begin
GetForm2( 99, False ).Close;
end;
Should be
procedure TForm1.Button3Click(Sender: TObject);
begin
GetForm2( 99, False ).Free;
end;
otherwise you will get an access violation if that form instance doesn't exist.
John Leavey
 

Re:Freeing Dynamically Created Forms

Pete <XXXX@XXXXX.COM>writes:
Quote
A further simplification can be made:

Move the MyForms array into TForm1's Public declarations. This will get the
array initialized automatically at startup, so the init code in Form2's
unit can be deleted. Form2's OnDestroy handler then needs to be modified to

Form1.MyForms[Index]:= nil;
That all looks fine. You can also get rid of the initialization
section code now.
Good luck.
Kurt
 

Re:Freeing Dynamically Created Forms

On Thu, 13 Nov 2003 16:23:47 -0800, Pete<XXXX@XXXXX.COM>said ...
Quote

5. How can Form1 tell this has happened?
All component subclasses (including forms) have a notification mechanism
you could take advantage of:
procedure TForm1.Button1Click(Sender: TObject);
begin
if MyForms[3] = nil then begin
{$IFDEF Debug} ShowMessage('Creating'); {$ENDIF}
MyForms[3]:= TForm2.Create(Form1);
// MyForms[3].Index:= 3;
MyForms[3].FreeNotification(self);
end;
MyForms[3].Show;
end;
procedure TForm1.Notification(AComponent: TComponent; Operation:
TOperation); {Must be an override method}
var i:integer;
begin
if Operation=opRemove then
for i := low(MyForms) to high(MyForms) do
if MyForms[i] = AComponent then
begin
MyForms[i] := nil;
break;
end;
end;
Another options is to remember that if you create a form with yourself as
the owner then you can always look for the forms you created in your
components collection. Forms are automatically removed from this
collection when free'd.
Marc
 

Re:Freeing Dynamically Created Forms

Thanks all for your great suggestions. You've covered some new ground for
me!