Delphi – Strange “A component named TFrm1 already exists” error

delphi

I want to let the user to create multiple instances of the same form (let's call it Form1 which is a MDI child form). So I have two procedures like this where I create the forms.

procedure MyProcedure1;           // procedure 2 is similar. it also has a var called MyFrm
var MyFrm: TFrm1;
begin
  ... 
  MyFrm:= TFrm1.create(MainForm);
  MyFrm.BringToFront;
  MyFrm.LoadFromFile(someFile);
end;

As you can see MyFrm is local var. This is ok for me as I don't need to programatically access the form after I create it. There is no other global variable named Frm1. In the OnClose event of MyFrm I have Action:= caFree;

What could cause the error above?
A user sent that error. It happened only once and I cannot reproduce it.


Edit:

  1. The error appears in the "MyFrm:= TFrm1.create" line.

  2. Some people suggested that I need to programatically give unique names to my dynamically created forms. I also wondered myself what name a form takes when it is created so I stepped into the code while calling the MyProcedure1 procedure.
    Delphi automatically gives unique names like
    MyFrm.name= MyFrm, then
    MyFrm.name= MyFrm_1,
    MyFrm.name= MyFrm_2,
    MyFrm.name= MyFrm_3, and so on.

  3. The MyFrm.Name is not altered in LoadFromFile. I have checked (breakpoint) the value of 'MyFrm.Name' at the end of procedure MyProcedure1; after LoadFromFile. The name is unique.

  4. As some people suggested, I override the SetName procedure and checked the name of TMyFrm. Indeed each form gets a unique name.

    procedure TMyFrm.SetName(const Value: TComponentName);
    begin
    ShowMessage(Value);
    inherited;
    end;

  5. I have many forms in this app but only the MainForm is auto-created.

  6. I don't use threads. Anyway this will not be relevant since the forms are created by user (so multi-threading is irrelevant unless the user can create 2 forms at the same time).

Best Answer

Giving MainForm as the Owner in TFrm1.Create will include the newly created form in the components list of MainForm. A component ensures that this list doesn't contain any two components with the same non-empty name (otherwise FindComponent won't work). This mechanism also works when a component changes its name.

As long as you don't specify the name in TFrm1.Create it is most likely that it is set by the LoadFromFile method, which means that you don't have much influence on the name unless you change the file's content.

A valid workaround is to create the form with nil as Owner, load the form from the file, change the name to a unique value or to an empty string and finally call MainForm.InsertComponent.

procedure MyProcedure1;           
var MyFrm: TFrm1;
begin
  ... 
  MyFrm:= TFrm1.create(nil);
  MyFrm.BringToFront;
  MyFrm.LoadFromFile(someFile);
  MyFrm.Name := ''; // or some unique name
  MainForm.InsertComponent(MyFrm);
end;
Related Topic