AzharR
AzharR

Reputation: 13

Free multiple dynamically created frames in a loop

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

Answers (1)

Andreas Rejbrand
Andreas Rejbrand

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;

What's wrong with your code?

But I haven't explained what is wrong with your code.

My guess is that

  1. the actual error message is "A component named frmFlightDisplay1 already exists",
  2. the exception happens not when you try to remove frames, but when you try to add them again, and
  3. the code blocks adding and removing the frames aren't in methods of 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

Related Questions