When you add a new form in Lazarus project, it will be created automatically when application starts.
If you want to create forms only when needed to reduce memory consumption, or to create many instances of that form during application execution, you can create it at run time using the following procedures:
Suppose that your form name is fmSecondForm
- Remove the form from Auto-create forms list in Project Options/Forms, and put it in Available forms
2. At Form’s OnClose event write this code to free this instance when closing it:
CloseAction:= caFree;
3. Whenever you need to create this form write this code :
procedure TfmFirst.Button1Click(Sender: TObject); var MyForm: TfmSecondForm; begin MyForm:= TfmSecondForm.Create(Application); MyForm.Show; end;
This was VERY helpful…..thank you!
However, how do you programmatically perform a graceful close on one of these forms? I tried a button on the form with an onClick event set to this:
LoginForm.Close;
freeandnil(LoginForm);
and I get an Access Violation error.
Suggestions?
Well…..
I think I got it…..was trying to call close like ‘myForm.Close’, but simply putting ‘Close’ in my function did the trick.
And, since caFree was added to the onClose event, it should be clean, right?
Yes, close triggers OnClose event of the form, in which we allow it to be released from memory.
If you are inside the form, you can do self.close
or just close, but myForm variable/object can not be accessed from TfmSecondForm. myForm is a local variable inside Button1Click procedure and it will not be accessible from any other procedure.
You can call This procedure (Button1Click) 10 times, for example, and it will create 10 instances of TfmSecondForm, myForm will point to the last one, then it will disappear when function finishes.
You can access that forms using Application.Components or Application.Controls (I’m not sure which one)
Motaz
Thank you.
I think I understand a little better.
I really appreciate your examples.
Keith
//You can access forms using Application.Components
Screen.FindForm() is useful for known frm.Name values
You can recursively traverse Application.Components and test for known .ClassName values or custom Tag values (a PtrInt type).
There are no Application.Controls or .Forms members. This is the gist of getting all components from all forms:
This leads me to the topic of how can to instantiate a form within a container control like a tab? I used to do this all the time in VB and GAMBAS, can’t seem to get a handle (sic) on it in Free Pascal/Lazarus.
I have made a simple application for you to illustrate this method.
you can download it from here:
http://code.sd/samples/Container.zip
1. In main form put page control and add tab sheet on it
2. Add second form, and remove it from project options/forms/auto create
3. In second form’s OnClose event write:
4. Add a button to main form and put this code on it:
Excellent. That clears things up nicely. Thank you very much.
In my small test harness I’ve got two Units, UnitForm1 and UnitForm2. UnitForm1 has a PageControl with two TabSheets. UnitForm1 is AutoCreated, UnitForm2 is not.
UnitForm2 has a button control and an Edit control.
On FormCreate of UnitForm1 an instance of UnitForm2 is brought into existence on the first TabSheet (this I did just to make sure I had it right). A button on UnitForm1 creates another instance of UnitForm2, this time on TabSheet2 of PageControl1. The click event of that Button has a local variable ‘MyForm1’ of type ‘TForm2’, which (I think) should get released when the click event procedure terminates.
OK, so now there are two forms instantiated, one on each of the two TabSheets of a PageControl. How can I reference the text in the edit controls of either of the instantiated forms? I see the instantianted form as a control (TabSheet2.ControlCount returns 1), I see the controls on the instantiated form as components (TabSheet2.Controls[0].ComponentCount returns 2 (the button and the edit field)). If I enter text into the edit field how can I reference it from UnitForm1? Component[1] of TabSheet2.Controla[0] is the edit field, I want to do something like
StrValue := TabSheet2.Controls[0].Components[1].Text;
My Pascal is from the mid to late eighties, I’ve been using VB and GAMBAS for the last 10 years so my thinking is not as controlled as it perhaps should be.
On a new button on the first form (AutoCreated form) I write this code:
and on creating the form I put TabSheet1 as owner instead of Nil:
Yes! This
(TabSheet1.Components[x] as TfmSecond).Edit1.Text
is non-obvious, thank you very much أبو إياس.
Excelente artigo. Muitíssimo Obrigado!
Brazil
Some really useful stuff here. Here’s another approach you might like:
procedure TfmMain.Button2Click(Sender: TObject);
var
i: Integer;
myForm: TfmSecond;
begin
for i:= 0 to TabSheet1.ComponentCount – 1 do
if TabSheet1.Components[i] is TfmSecond then
myForm := TfmSecond(TabSheet1.Components[i]);
ShowMessage(myForm.Edit1.Text);
end;
Sorry, I couldn’t work out how to edit my post, so I’m repeating it with corrections
Some really useful stuff here. Here’s another approach you might like:
procedure TfmMain.Button2Click(Sender: TObject);
var
i: Integer;
myForm: TfmSecond;
begin
for i:= 0 to TabSheet1.ComponentCount – 1 do
if TabSheet1.Components[i] is TfmSecond then
begin
myForm := TfmSecond(TabSheet1.Components[i]);
ShowMessage(myForm.Edit1.Text);
end;
end;