Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange "A component named TFrm1 already exists" error

Tags:

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).

like image 359
Server Overflow Avatar asked Feb 01 '11 11:02

Server Overflow


3 Answers

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;
like image 84
Uwe Raabe Avatar answered Oct 22 '22 16:10

Uwe Raabe


The message is caused because each form must be uniquely named.

When you create a form twice, you need to ensure each instance has a unique name, or set the Name to an empty string. The latter also is the trick when using multiple instances of a data module, so that the automatic linking of data-aware controls does not end up always using the first instance.

Add

MyFrm.Name := MyFrm.Name + <something unique>;

MyFrm.Name := '';

after the Create call and you should be fine

like image 38
Marjan Venema Avatar answered Oct 22 '22 17:10

Marjan Venema


MyFrm.Name is the same for both instances...

Make sure than MyFrm.Name is unique...

like image 43
Jørn E. Angeltveit Avatar answered Oct 22 '22 16:10

Jørn E. Angeltveit