Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I avoid to call Application.CreateForm twice?

I stumbled on this page Why shouldn’t I call Application.CreateForm. Now I have some code like this:

SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update; // force update
Application.Initialize;
Application.CreateForm(TClientData, ClientData);
SplashForm.Update; // force update
Application.CreateForm(TClientMainForm, ClientMainForm);
Application.ShowHint := True;

Application.Run;
ClientMainForm.ServerConnected := false;
FreeAndNil(ClientMainForm);
FreeAndNil(ClientData);

First a splashform is created, then a datamodule and last the main form. The page says that Application.CreateForm should not be called twice. Should the code above be changed?

like image 945
Roland Bengtsson Avatar asked Feb 01 '10 07:02

Roland Bengtsson


3 Answers

There is nothing wrong with using Application.CreateForm multiple times. But this introduces global variables for each form which can be a code smell. Unfortunately the IDE creates one for each form. Although you can remove them if you like.

A better way is to create a form when you need it and release it when you are ready with it. So you only use Application.CreateForm for the main form.

A main datamodule can be created by the main form. But it can be global too, just a matter of taste.

So to answer the question, you can avoid Application.CreateForm by creating and releasing the forms locally.

The article mentions the side effect of Application.CreateForm (the first completed form is the main form). So there can be unexpected side effects if the main form creates other forms using Application.CreateForm.

So just to avoid any nastyness, you should limit yoursef to a single call. Which is done using only one global form.

like image 123
Toon Krijthe Avatar answered Sep 30 '22 15:09

Toon Krijthe


If TClientData is a Data Module and TClientMainForm is a form, then no (except perhaps the two FreeAndNil calls at the end - not really needed). But take care. Because as it says Rob Kennedy in his post, the Application.CreateForm does other things behind (it sets the MainForm variable), so I would advise to set up your project file according to the following rules:

  1. Create all the forms which you want to create at startup using a single call to Application.CreateForm - usually this is done by the IDE.

  2. Remove from the project file the forms which you want to create dynamically (on-demand) in your program. (In Project | Options | Forms...) - move them from 'Auto-Create Forms' to 'Available Forms'

  3. Create your forms in your code using TmyForm.Create(Owner) (etc.) and not with Application.CreateForm(...). As an aside, if you are sure that you will free the form, then it is better (in order to speed the things up) to call TmyForm.Create(nil) - iow without any owner.

  4. If you want to do some kind of initialization at startup you can have a procedure / method in the project file tied to a form / data module already created and run it before application run.

For example:

begin 
  Application.Initialize; 
  Application.MainFormOnTaskbar := True; 
  Application.CreateForm(TdmoMain, dmoMain); //<--this is a data module
  Application.CreateForm(TfrmMain, frmMain); //<--this will became the main form
  Application.CreateForm(TfrmAbout, frmAbout);
  //... other forms created here...
  frmMain.InitEngine; //<--initialization code. You can put somewhere else, according with your app architecture
  Application.Run;
end.

In this way you will have the project file clean and you will know exactly which is which.

HTH

like image 37
John Thomas Avatar answered Sep 30 '22 15:09

John Thomas


When I wrote that article, I was thinking primarily of code outside the DPR file. People see the form-creation code generated by the IDE in the DPR file and think that's the best way to create forms generally, so they use that elsewhere in their programs. They sometimes use it in the main form's OnCreate event handler to create other forms their program needs, and then they hit problems because the program's main form isn't what they think it is.

In the code you provided, it's easy to call CreateForm just once. Use it for the main form, and for nothing else. The data module isn't a main form, so you don't need CreateForm's magic there.

SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update; // force update
Application.Initialize;

// Change to this.
ClientData := TClientData.Create(Application);

SplashForm.Update; // force update
Application.CreateForm(TClientMainForm, ClientMainForm);
Application.ShowHint := True;

Application.Run;
ClientMainForm.ServerConnected := false;

// Remove these.
FreeAndNil(ClientMainForm);
FreeAndNil(ClientData);

You really shouldn't free the objects you've created here because you don't own them. They're owned by the global Application object, so let it take care of freeing them: Remove the two calls to FreeAndNil.

like image 20
Rob Kennedy Avatar answered Sep 30 '22 16:09

Rob Kennedy