How to create forms at run time in Free Pascal/Lazarus

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

  1. 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;
Advertisement

15 thoughts on “How to create forms at run time in Free Pascal/Lazarus

  1. 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?

  2. 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?

  3. 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

  4. //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:

    procedure DoOp(AComponent: TComponent);
    
    var i,j : integer;
    
    begin
      for i := 0 to AComponent.ComponentCount - 1 do
       DoOp(AComponent.Components[i]);
    
      if AComponent is TForm then
        TForm(AComponent).Caption := 'Me Form!';
    
      if AComponent is TLabel then
        TLabel(AComponent).Caption := 'Me Label!';
    
      if AComponent is TButton then
        TButton(AComponent).Caption := 'Me Button!';
    
    //  if AComponent is TCheckBox then ...
    //  if AComponent is TRadioButton then ...
    //  if AComponent is TTabSheet then ,,,
    //  if AComponent is TGroupBox then ,,,
      if AComponent is TMenuItem then 
        TMenuItem(AComponent).Caption := 'Me MenuItem!';
    
      if AComponent is TComboBox then
        for j := 0 to TComboBox(AComponent).Items.Count - 1 do
          TComboBox(AComponent).Items[j] := 'Me TString!';
    
      if AComponent is TListview then
        with AComponent as TListview do begin
          for j := 0 to Columns.Count - 1 do
            Columns[j].Caption := 'Me LVColHeader!';
        end;
    end;
    
  5. 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.

  6. 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:

    CloseAction:= caFree;
    

    4. Add a button to main form and put this code on it:

    procedure TfmMain.Button1Click(Sender: TObject);
    var
      SecondForm: TfmSecond;
    begin
      SecondForm:= TfmSecond.Create(nil);
      SecondForm.Parent:= TabSheet1;
      SecondForm.BorderStyle:= bsNone;
      SecondForm.Align:= alClient;
      SecondForm.Show;
    
    end;
    
  7. 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.

  8. On a new button on the first form (AutoCreated form) I write this code:

    procedure TfmMain.Button2Click(Sender: TObject);
    var
      i: Integer;
    begin
      for i:= 0 to TabSheet1.ComponentCount - 1 do
      if TabSheet1.Components[i] is TfmSecond then
        ShowMessage((TabSheet1.Components[i] as TfmSecond).Edit1.Text);
    
    end;
    
    

    and on creating the form I put TabSheet1 as owner instead of Nil:

      SecondForm:= TfmSecond.Create(TabSheet1);
      SecondForm.Parent:= TabSheet1;
    
  9. Yes! This

    (TabSheet1.Components[x] as TfmSecond).Edit1.Text

    is non-obvious, thank you very much أبو إياس.

  10. 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;

  11. 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;

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s