Reputation: 13
I have a program where on one of the forms I need to dynamically create an unspecified number of frames (determined by the number of records after a database search) at runtime. I have done this using a for loop, and each newly created frame is assigned a new name like so:
for K:=1 to qryAeroAir.RecordCount do
begin
frmFlightDisplay:=TfrmFlightDisplay.Create(frmBook);
frmFlightDisplay.Name:='frmFlightDisplay'+IntToStr(K);
frmFlightDisplay.Parent:=pnlBookingDisplay;
// Rest of code for frame properties
end;
This seems to work fine, however later on in the program I need to free or destroy these frames before creating new ones (with the same sort of loop) and this is where I am having a problem.
for I:=1 to qryAeroAir.RecordCount do
begin
TfrmFlightDisplay(FindComponent('frmFlightDisplay'+IntToStr(I))).Free;
end;
The above code gives me an error 'Frame with the name frmFlightDisplay1' already exists, which leads me to believe that the frames are not actually being destroyed. Is there any other way to free or destroy all of the frames which will prevent this from occurring?
Upvotes: 1
Views: 412
Reputation: 109098
Managing dynamically created objects using string manipulation is fragile and not very performant.
A much better approach, IMHO, is to store the frames in an object list. Declare, in your form class, a private field (uses Generics.Collections
)
private
FFrames: TObjectList<TfrmFlightDisplay>;
and create and destroy it with the form:
procedure TForm1.FormCreate(Sender: TObject);
begin
FFrames := TObjectList<TfrmFlightDisplay>.Create(True {owns objects});
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FFrames.Free;
end;
Then, when you create a new frame, you put it in this list, which will then own it:
procedure TForm1.btnAddFlightDisplayClick(Sender: TObject);
var
frm: TfrmFlightDisplay;
begin
frm := TfrmFlightDisplay.Create(nil);
try
frm.Parent := Self;
// other code settings properties of frm
except
frm.Free;
raise;
end;
FFrames.Add(frm); // transfer of ownership
end;
To free all frames, simply remove them from the list. Since the list owns the frames, this will free the frame objects:
procedure TForm1.btnClearClick(Sender: TObject);
begin
FFrames.Clear;
end;
But I haven't explained what is wrong with your code.
My guess is that
frmBook
.If my guesses are correct, the problem is that you clearly let the frames be owned by frmBook
(the argument given to the frame's constructor), but FindComponent
is a shorthand for Self.FindComponent
and if this isn't run in a method of frmBook
, then Self
isn't frmBook
. Therefore, FindComponent
will return nil
and Free
will have no effect. (Fortunately, X.Free
is safe even if X
is nil
.)
Consequently, a quick fix to your problem would be to write frmBook.FindComponent
instead.
Upvotes: 2