Jump to content
Mike Torrettinni

How to create common CreateForm method?

Recommended Posts

To speed up the opening of my project, I want to only create forms when needed.

So, I commented out all CreateForm in Project source, except for Main form. And now when I need to open a form, I use:

 

 

if Form2 = nil then
    Application.CreateForm(TForm2, Form2);
 Form2.ShowModal;

this works good.. but, now I have this code all over the Main form unit.

 

Is it possible to have a common method like AccessForm(Form2, True); that would do this code in a method, for all forms:

 

procedure AccessForm(aForm: TForm; aModal: boolean); 
begin
  if aForm = nil then
	Application.CreateForm(aForm, ??? TForm... ??? ); // <-- how to determine what type aForm is?
  if aModal then
    aForm.ShowModal
  else
    aForm.Show;
end;

// open Form
AccessForm(Form2, True);

 

Is something like this possible? If yes, how I can find out what class a Form is, even before is created?

Share this post


Link to post

Maybe something like this?

procedure AccessForm(var aForm: TForm; aClass: TFormClass; aModal: Boolean);
begin
  if not Assigned(aForm) then aForm := aClass.Create(Application);
  Assert(Assigned(aForm)); // just in case :-)
  if aModal then aForm.ShowModal else aForm.Show;
end;
.....
AccessForm(Form2, TForm2, True);
....

 

Share this post


Link to post

Nice, this is better. Is it possible to do this somehow without Form class? I assume not, since we can't get Form's class, if it's nil (not created yet), right?

Share this post


Link to post
3 hours ago, Mike Torrettinni said:

I commented out all CreateForm in Project source, except for Main form.

There is option in the project options (Application | Forms) to select what forms to create when an application starts.

Edited by Kryvich

Share this post


Link to post

The entire design seems wrong. Don't use a global variable for each form. Remove them. Use a local variable and destroy it when ShowModal returns. 

  • Like 2

Share this post


Link to post
  TfrmBase = class (TForm)
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    class function Execute : boolean; virtual;
  end;

class function TfrmBase.Execute: boolean;
var Form: TfrmBase;
begin
 Form:=Create(nil);
 try
  Result:=Form.showModal<>mrCancel;
 finally
  Form.Release;
 end;
end;

Inherit your forms from a BaseClass like this on.  If you need an other behavior in the actual class you override the class-function. 

  • Like 2

Share this post


Link to post
2 hours ago, ConstantGardener said:

  TfrmBase = class (TForm)
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    class function Execute : boolean; virtual;
  end;

class function TfrmBase.Execute: boolean;
var Form: TfrmBase;
begin
 Form:=Create(nil);
 try
  Result:=Form.showModal<>mrCancel;
 finally
  Form.Release;
 end;
end;

Inherit your forms from a BaseClass like this on.  If you need an other behavior in the actual class you override the class-function. 

How do I open a form with this approach?

Share this post


Link to post
4 hours ago, Alexander Elagin said:

Maybe something like this?


procedure AccessForm(var aForm: TForm; aClass: TFormClass; aModal: Boolean);
begin
  if not Assigned(aForm) then aForm := aClass.Create(Application);
  Assert(Assigned(aForm)); // just in case :-)
  if aModal then aForm.ShowModal else aForm.Show;
end;
.....
AccessForm(Form2, TForm2, True);
....

 

Just in case... What case? 

If construction of the form fails you will never hit Assert line anyway.

Share this post


Link to post

I prefer caFree action in onClose so I don't have to deal with references at all and doesn't really care if it's shown modal or not.

Of course this means the forms are always (re)-created.

Edited by Attila Kovacs

Share this post


Link to post
5 hours ago, Kryvich said:

There is option in the project options (Application | Forms) to select what forms to create when an application starts.

Thanks. I never used that option, always changing this in project source.

Share this post


Link to post
9 hours ago, Dalija Prasnikar said:

Just in case... What case? 

If construction of the form fails you will never hit Assert line anyway.

Have you seen the smile in my comment? I'd never use such design myself (for modal forms I use class functions, non-modal always have their own local variables). This was just an in-situ snippet to show how to use TFormClass and polymorphism in action based on the originally posted code.

Share this post


Link to post
1 hour ago, Alexander Elagin said:

Have you seen the smile in my comment? I'd never use such design myself (for modal forms I use class functions, non-modal always have their own local variables). This was just an in-situ snippet to show how to use TFormClass and polymorphism in action based on the originally posted code.

Some people will just copy paste anything smile or no smile 😉

 

Share this post


Link to post
6 hours ago, David Heffernan said:

Can't work out how to delete this post in mobile interface.... 

You can't delete a post at all. The way to go is to report it so an admin can delete it.

Share this post


Link to post
22 hours ago, Alexander Elagin said:

Maybe something like this?


procedure AccessForm(var aForm: TForm; aClass: TFormClass; aModal: Boolean);
begin
  if not Assigned(aForm) then aForm := aClass.Create(Application);
  Assert(Assigned(aForm)); // just in case :-)
  if aModal then aForm.ShowModal else aForm.Show;
end;
.....
AccessForm(Form2, TForm2, True);
....

 

I just couldn't make it work with var parameter, it kept giving me E2033 Types of actual and formal var parameters must be identical

So, I changed into a function:

 

function AccessForm2(aForm: TForm; aClass: TFormClass; aModal: Boolean = True): TForm;
begin
  if not Assigned(aForm) then
    Result := aClass.Create(Application)
  else
    Result := aForm;
  Assert(Assigned(Result)); // just in case :-)
  if aModal then Result.ShowModal else Result.Show;
end;

and I call it with:

 

Form2 := AccessForm2(Form2, TForm2, True) as TForm2;

Seems a bit complicated... am I doing something wrong?

Share this post


Link to post
2 hours ago, Mike Torrettinni said:

I just couldn't make it work with var parameter, it kept giving me E2033 Types of actual and formal var parameters must be identical

So, I changed into a function:

 


function AccessForm2(aForm: TForm; aClass: TFormClass; aModal: Boolean = True): TForm;
begin
  if not Assigned(aForm) then
    Result := aClass.Create(Application)
  else
    Result := aForm;
  Assert(Assigned(Result)); // just in case :-)
  if aModal then Result.ShowModal else Result.Show;
end;

and I call it with:

 


Form2 := AccessForm2(Form2, TForm2, True) as TForm2;

Seems a bit complicated... am I doing something wrong?

There is one loophole in your design: if a form is destroyed it will not automatically set the form variable the designer created for it to nil. I can only stress what has been posted in some of the other replies: do not use the form variables for forms that are not autocreated!

Delete the variable declaration directly after the form unit has been created by the designer. Use local variables for modal forms (or a class method to create, use, and destroy them). For modeless forms you can always find existing instances by looking at the Screen.Forms array, or have the main form (which presumably creates such a form on an as needed basis) keep the reference in a private field.

The automatic form variables encourage some bad programming practices (like accessing controls on another form directly) and become worse than useless if you have to have more than one instance of a given form class open at the same time.

  • Like 1
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×