By default (that is: with all default IDE configuration settings), newly designed forms are automatically created. Only the main form will be shown, and secondary forms can be shown with:
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Show;
Form3.ShowModal;
end;
It is good common practice to disable this auto creation option. Go to: Tools > (Environment) Options > (VCL) Designer > Module creation options, and disable/uncheck the Auto create forms & data modules option.
Instead, create an (already designed) form only when it is needed:
procedure TForm1.Button1Click(Sender: TObject);
var
Form: TForm2;
begin
Form := TForm2.Create(Self);
Form.Show;
end;
This illustrates as well that the global variables for the secondary forms are not needed, and it is good common practice to delete them as soon as possible to prevent wrong usage:
type
TForm2 = class(TForm)
end;
//var
// Form2: TForm2; << Always delete these global variable
implementation
If you do not want to set up such secondary form with the form designer, then you need to create all controls in code at runtime. As follows:
unit Unit2;
interface
uses
Classes, Forms, StdCtrls;
type
TForm2 = class(TForm)
private
FButton: TButton;
public
constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
end;
implementation
{ TForm2 }
constructor TForm2.CreateNew(AOwner: TComponent; Dummy: Integer = 0);
begin
inherited CreateNew(AOwner);
FButton := TButton.Create(Self);
FButton.SetBounds(10, 10, 60, 24);
FButton.Caption := 'OK';
FButton.Parent := Self;
end;
end.
As you see, I used the CreateNew
constructor. This is necessary for T(Custom)Form
derivatives:
Use CreateNew
instead of Create
to create a form without using the associated .DFM file to initialize it. Always use CreateNew
if the TCustomForm
descendant is not a TForm
object or a descendant of TForm
.
For all other container controls (such as TPanel
, TFrame
, etc.) you can override the default constructor Create
.
Call this form as follows:
procedure TForm1.Button1Click(Sender: TObject);
var
Form: TForm2;
begin
Form := TForm2.Create(nil);
try
Form.ShowModal;
finally
Form.Free;
end;
end;
Or:
procedure TForm1.Button1Click(Sender: TObject);
begin
FForm := TForm2.CreateNew(Application);
FForm.Show;
end;
In this last case, the form is not freed but hidden when it is closed, so you need to store its reference in a private field (FForm
) and free it later. Or you can do it automatically:
unit Unit2;
interface
uses
Classes, Forms, StdCtrls;
type
TForm2 = class(TForm)
private
FButton: TButton;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
public
constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
end;
implementation
{ TForm2 }
constructor TForm2.CreateNew(AOwner: TComponent; Dummy: Integer = 0);
begin
inherited CreateNew(AOwner);
OnClose := FormClose;
FButton := TButton.Create(Self);
FButton.SetBounds(10, 10, 60, 24);
FButton.Caption := 'OK';
FButton.Parent := Self;
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
end.
Now, you could call it without storing a reference:
procedure TForm1.Button1Click(Sender: TObject);
begin
TForm2.CreateNew(Self).Show;
end;
Whether you pass, Self
, Application
or nil
as owner for the new form depends on when you want to get it automatically destroyed in case you do not free it manually or via the OnClose
event. Using
Self
: will destroy the new form when the calling form is destroyed. This is particularly usefull when the calling form isn't the main form.
Application
: will destroy the new form when the application ends. This would be my preferred choice.
nil
: will not destroy the new form and results in a memory leak at application's finish. Though, memory will ultimately be released when Windows kills the process.
Dialogs.CreateMessageDialog
for an example. – Magic