0

This is a continuation of my question: How can I display a Delphi form in a panel?

I want to use a forms global variable to embed it in a panel to display it now, but it only creates the form to embed, without it's buttons.

In the code of the executable I'm creating the form to embed first and the form that I want to embed it in second, like so:

program Project1;

uses
  System.StartUpCopy,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm2, Form2);
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

The main form's code is:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Panel1: TPanel;
    procedure EmbedForm(ArgParent : TControl; ArgForm : TCustomForm);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses Unit2;



procedure TForm1.FormCreate(Sender: TObject);
begin
  EmbedForm(Panel1, Form2);
end;

procedure TForm1.EmbedForm(ArgParent: TControl; ArgForm: TCustomForm);
begin
  while ArgForm.ChildrenCount>0 do
  begin
    ArgForm.Children[0].Parent:= ArgParent;
  end;
end;

end.

The code of the form to embed is:

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls;

type
  TForm2 = class(TForm)
    Button2: TButton;
    Button1: TButton;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

end.
Community
  • 1
  • 1
Friso
  • 2,328
  • 9
  • 36
  • 72
  • 1
    Your calls to CreateForm are backward. The first call to CreateForm determines which form is the application's main form, so you're trying to embed your main form into a child form, and that won't end up well for you. Is there a reason you're not either using frames, docking, or tabbed pages for what you're trying to do? – Ken White Feb 24 '15 at 13:43
  • If I do it in that order I get a `exception class $c00000005` message. I'm just trying things out now, so no special reason. – Friso Feb 24 '15 at 13:49
  • @Ken That's because the asker has followed the advice of the Embarcadero example. It does seem weird. Link is in prev Q. – David Heffernan Feb 24 '15 at 13:50
  • 1
    @Friso1990: when you correct the order of the `CreateForm()` calls so `Form1` is created before `Form2`, you are getting an AV because `Form1` is calling `EmbedForm()` to embed `Form2` before it has been created yet. **Fix that error** instead of trying to hide it. Either have `Form2` call `Form1.EmbedForm(Form1.Panel1, Self)`, or else create `Form2` before `Form1` by using `Form2 := TForm2.Create(Application)` instead of `Application.CreateForm(TForm2, Form2)` so `Form1` can still be assigned as the app's `MainForm`. – Remy Lebeau Feb 24 '15 at 19:42

1 Answers1

1

The way I've done this before to avoid having to iterate through all you ArgForm's children is by having a "master container" of sorts on the ArgForm that has all the children you need. How I set this up is by

  1. first placing a TLayout, aligned to Client on the ArgForm
  2. Next, I added all my children controls to the TLayout of ArgForm (buttons etc..)
  3. Next add a panel to the form we want this embedded in
  4. After that form was setup, I assign the layout of ArgForm to the Parent form's panel on ParentForm's OnShow rather than the OnCreate ( ArgForm.Children[0].Parent:=Self.Panel1;)

Project Source:

program Project1;

uses
  System.StartUpCopy,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {ParentForm},
  Unit2 in 'Unit2.pas' {ArgForm};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TParentForm, ParentForm);
  Application.CreateForm(TArgForm, ArgForm);
  Application.Run;
end.

Parent Form Code:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,unit2,
  FMX.StdCtrls;

type
  TParentForm = class(TForm)
    Panel1: TPanel;
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ParentForm: TParentForm;

implementation

{$R *.fmx}

procedure TParentForm.FormShow(Sender: TObject);
begin
  ArgForm.Children[0].Parent:=Self.Panel1;
end;

end.

ArgForm Code:

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Layouts;

type
  TArgForm = class(TForm)
    Layout1: TLayout;
    Button1: TButton;
    Button2: TButton;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ArgForm: TArgForm;

implementation

{$R *.fmx}

end.

Maybe someone else could answer, but it just seemed to me the reason why the buttons weren't showing on the create, was that the controls hadn't been created at that time?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
SmeTheWiz
  • 210
  • 1
  • 8
  • Well, now you have `ArgForm` as the applications MainForm (first created form by `Application`) – Sir Rufo Feb 24 '15 at 18:57
  • @SirRufo, thanks for pointing that out. I've changed it in my test app, recompiled, and it works just fine. I edited my post to reflect the new project source. – SmeTheWiz Feb 24 '15 at 19:19
  • 1
    You should call Application.CreateForm exactly once. For the main form. The rest of the time, call the constructor. – David Heffernan Feb 25 '15 at 07:41