I'm working with Delphi 7 and I'm trying to create a form programmatically. Here's my form class stub:
unit clsTStudentInfoForm;
interface
uses Forms;
type
TStudentInfoForm = class (TForm)
end;
implementation
end.
I also have a button on my main form (that's just a regular form that's supposed to create and show the form above at run-time) and when clicked it creates and shows the student form as a modal window. It does show the form but there's nothing on it. The only thing you can do is click the close button at the upper-right corner of the window to close it.
procedure TLibraryForm.btnShowStudentIfoFormClick(Sender: TObject);
var
f : TStudentInfoForm;
begin
f := TStudentInfoForm.CreateNew(Self);
f.ShowModal;
f.Free;
f := nil;
end;
I have no idea how to add components to a programmatically-created form (not at run-time, but to the source code). Can you help me write some code that adds an Okay button to the student form as well as sets the caption and the form's height and width (the code has to be written in the student form file)?
Any suggestions and examples will be highly appreciated. Thank you.
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 ofCreate
to create a form without using the associated .DFM file to initialize it. Always useCreateNew
if theTCustomForm
descendant is not aTForm
object or a descendant ofTForm
.
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.