Search code examples
delphifiremonkeyrad-studio

Inaccessible value from control on embedded child form (Delphi, Firemonkey)


I am new to Firemonkey (using RadStudio 10.3.2), and I am trying to update a listbox control on an embedded child form. However, when I try to access any of the properties of the listbox (ListBox1), they show up as "Inaccessible value." I'm pretty sure that I am missing something very simple. I appreciate any and all help! Thank you!

I have made the following, simplified application, to illustrate my issue.

Project1 Application Initialization:

program Project1;

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

{$R *.res}

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

Unit1.pas:

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.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure EmbedForm(AParent:TControl; AForm:TCustomForm);
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses Unit2;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Embed Scenarios Form
  EmbedForm(Panel1, TForm2.Create(Self));
  Panel1.Visible := true;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  data_strings: array of string;
begin
  // Assign strings
  SetLength(data_strings, 2);
  data_strings[0] := 'Hello';
  data_strings[1] := 'World';
  // Load strings
  Form2.ListBox1.Items.Add(data_strings[0]);
  Form2.ListBox1.Items.Add(data_strings[1]);
end;

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

end.

Unit2.pas:

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.Layouts,
  FMX.ListBox;

type
  TForm2 = class(TForm)
    ListBox1: TListBox;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

end.

The Form2.ListBox1 properties are inaccessable to the code on the main form--see the Button1Click event. I have the parent form (Unit1) using the child form (Unit2).

I am at a loss as to why an enabled, visible component is inaccessible. My modal forms never had this issue, so I think it has to do with the embedded child form.


Solution

  • The error is that you don't keep hold of a reference to the TForm2 instance. Yet you attempt to use the global Form2 variable later in the program.

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      // Embed Scenarios Form
      EmbedForm(Panel1, TForm2.Create(Self)); 
      Panel1.Visible := true;
    end;
    

    TForm2.Create(self) returns a reference, which you pass to EmbedForm() but then you loose it. Later in Button1.Click you call Form2.ListBox1.Items.Add(data_strings[0]); but Form2 is not assigned.

    Change that to e.g.:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      // Embed Scenarios Form
      Form2 := TForm2.Create(Application); // Use `Form2` name declared in `Unit2`
      EmbedForm(Panel1, Form2);
      Panel1.Visible := true;
    end;