Контроль встроенной формы не обновляется (Delphi, Firemonkey)

У меня есть очень простой проект Firemonkey (RadStudio 10.3.3), который я создаю, чтобы протестировать определенные варианты макета для будущего проекта. В прошлом с VCL я использовал модальные формы. Тестируемый проект использует панели (Panel1 и Panel2) в основной форме (Form1) для встраивания двух дополнительных форм (Form2 и Form3). Две встроенные формы состоят из одного списка (ListBox1) в каждой форме. Панели на основной форме накладываются друг на друга, поэтому я использую свойство «Видимость», чтобы отобразить нужную встроенную форму. Весь код находится на главной форме.

У меня проблема в том, что когда я переключаюсь между Form2 и Form3, строки, загруженные в список на Form3, никогда не появляются. Я пробовал Repaint в списке и панели, InvalidateRect в списке, SetFocus на панели и т. д., за которыми следуют Application.ProcessMessages. Ничего не работает успешно.

Основной код:

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;
    Panel2: TPanel;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    procedure EmbedForm(AParent:TControl; AForm:TCustomForm);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses Unit2, Unit3;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Embed Form2 in Panel1
  Application.CreateForm(TForm2, Form2);
  EmbedForm(Panel1, Form2);
  Panel1.Visible := true;

  // Embed Form3 in Panel2
  Application.CreateForm(TForm3, Form3);
  EmbedForm(Panel2, Form3);
  Panel2.Visible := false;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Populate ListBox1 on Form2 - the LOAD button
  Form2.ListBox1.Items.Add('Hello');
  Form2.ListBox1.Items.Add('World');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  // Hide Panel1 (Form2) and show Panel2 (Form3)
  Panel1.Visible := false;
  Panel2.Visible := true;
  // Populate ListBox1 on Form3
  Form3.ListBox1.Items.Add('Goodbye');
  Form3.ListBox1.Items.Add('World');
  // Repaint (Here's why I have tried various things to get the listbox strings to show up)
  //Panel2.Repaint;
  //Form3.ListBox1.Repaint;
  //Application.ProcessMessages;
end;

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

end.

Форма2 выглядит следующим образом:

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.

Форма 3 выглядит следующим образом:

unit Unit3;

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

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

var
  Form3: TForm3;

implementation

{$R *.fmx}

end.

Файлы .fmx находятся ниже по запросу.

Unit1.fmx (Форма1):

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 480
  ClientWidth = 640
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnCreate = FormCreate
  DesignerMasterStyle = 0
  object Button1: TButton
    Position.X = 232.000000000000000000
    Position.Y = 448.000000000000000000
    TabOrder = 1
    Text = 'Load'
    OnClick = Button1Click
  end
  object Button2: TButton
    Position.X = 328.000000000000000000
    Position.Y = 448.000000000000000000
    TabOrder = 2
    Text = 'Next'
    OnClick = Button2Click
  end
  object Panel1: TPanel
    Align = Center
    Size.Width = 640.000000000000000000
    Size.Height = 393.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
  end
  object Panel2: TPanel
    Position.Y = 43.000000000000000000
    Size.Width = 640.000000000000000000
    Size.Height = 393.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 4
  end
end

Unit2.fmx (Форма2):

object Form2: TForm2
  Left = 0
  Top = 0
  Caption = 'Form2'
  ClientHeight = 480
  ClientWidth = 640
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  DesignerMasterStyle = 0
  object ListBox1: TListBox
    Align = Center
    TabOrder = 0
    DisableFocusEffect = True
    DefaultItemStyles.ItemStyle = ''
    DefaultItemStyles.GroupHeaderStyle = ''
    DefaultItemStyles.GroupFooterStyle = ''
    Viewport.Width = 200.000000000000000000
    Viewport.Height = 200.000000000000000000
  end
end

Unit3.fmx (Форма3):

object Form3: TForm3
  Left = 0
  Top = 0
  Caption = 'Form3'
  ClientHeight = 480
  ClientWidth = 640
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  DesignerMasterStyle = 0
  object ListBox1: TListBox
    Position.X = 8.000000000000000000
    Position.Y = 8.000000000000000000
    TabOrder = 1
    DisableFocusEffect = True
    DefaultItemStyles.ItemStyle = ''
    DefaultItemStyles.GroupHeaderStyle = ''
    DefaultItemStyles.GroupFooterStyle = ''
    Viewport.Width = 196.000000000000000000
    Viewport.Height = 196.000000000000000000
  end
end

Опять же, Form2 и Form3 содержат только список (Listbox1 на обоих) и никакого дополнительного кода. Я просто запускаю исполняемый файл, нажимаю Button1, чтобы отобразить Hello World, затем нажимаю Button2, чтобы переключать панели и отображать вторую форму и ее список. Поскольку я новичок в Firemonkey, я уверен, что упускаю что-то простое. Спасибо за любую помощь!


Решение было очень простым. Мне пришлось удалить события CreateForm для Form2 и Form3 из настроек инициализации проекта — глупая ошибка с моей стороны. Он терял ссылку на эти формы во время выполнения.


person Ric Crooks    schedule 09.12.2019    source источник
comment
FWIW, гораздо проще поместить встроенный контент внутри TLayout в исходной форме, а затем просто назначить родителя этого макета.   -  person Jerry Dodge    schedule 09.12.2019
comment
@ Джерри Спасибо за информацию. Я начну изучать контейнер TLayout. Я использовал TPanel, потому что был знаком с ними в VCL.   -  person Ric Crooks    schedule 09.12.2019
comment
Я удалил свой ответ на данный момент, поскольку вы утверждаете, что это не так. Однако, поскольку дочерние родительские отношения не видны из кода, который вы разместили, я прошу вас добавить три файла .fmx к вашему вопросу. Я также должен подчеркнуть, что я не могу воспроизвести вашу проблему, если панели являются прямыми дочерними элементами Form1. Это просто работает так, как вы задумали.   -  person Tom Brunberg    schedule 10.12.2019
comment
@ Том, я сделал, как ты просил. Как видите, с Unit2 (Form2) или Unit3 (Form3) ничего не происходит. Поскольку вы не можете дублировать проблему, я просто создам еще один тестовый пример с нуля и посмотрю, существует ли проблема. У меня есть основная часть программного обеспечения, в которой также есть эта проблема, поэтому я создал простой тестовый код, чтобы проверить мою проблему. Как я уже упоминал, я только что обновился до RadStudio 10.3.3, хотя у меня была такая же проблема в 10.3.2. Спасибо за вашу помощь!   -  person Ric Crooks    schedule 10.12.2019
comment
Спасибо за обновления. Но теперь у вас есть в Unit1.fmx TPanel, а в дочернем элементе этой панели — TLayout, которого нет в Unit1.pas. Ото, у тебя вообще нет Panel2 в Unit1.fmx! Итак, Unit1.fmx не соответствует Unit1.pas. Если вы изменили вторую панель на TLayout (по предложению Джерри), и эта панель была на месте текущего макета, то это именно то, что я сказал в своем ответе. Этот Panel2 был ребенком Panel1, а не ребенком Form1. Что дает именно то поведение, которое вы описали. Вы согласны?   -  person Tom Brunberg    schedule 10.12.2019
comment
Убедились ли вы, что код, который вы сейчас опубликовали, действительно демонстрирует ошибку, о которой вы заявляете? У меня такое чувство, что это просто пустая трата времени!   -  person Tom Brunberg    schedule 10.12.2019
comment
@Tom - Да, это код, и он все еще не работает. Приложение запускается и корректно отображает Form2 в Panel1 (список пуст). Нажатие Button1 затем правильно заполняет список строками Hello и World. Затем при нажатии на кнопку Button2 правильно отображается Form3 в Panel2, но список пуст. Он никогда не показывает Goodbye and World. Я пробовал Repaint и т. д., как я уже отмечал выше. И мои разработки, и тестовые приложения (опубликованные выше) демонстрируют такое поведение. Я приношу извинения за публикацию неправильного файла .fmx ранее. Это была полностью моя вина.   -  person Ric Crooks    schedule 10.12.2019
comment
Кроме того, FWIW, я компилирую Windows 32-бит. Я также пробовал 64-разрядную версию Windows и наблюдал такое же поведение. По какой-то причине (вероятно, я упустил что-то простое) список в Form3 не будет отображать свое содержимое.   -  person Ric Crooks    schedule 10.12.2019
comment
Ну, как я и думал, просто пустая трата времени. Код, который вы показали, не указывает на проблему. А в нынешнем коде нет ничего, что даже могло бы, как ни посмотри.   -  person Tom Brunberg    schedule 10.12.2019
comment
@ Том Извини за твое разочарование, Том. Однако ваше разочарование заставило меня копаться в файлах .fmx и в конце концов найти проблему! Спасибо, что терпите меня!   -  person Ric Crooks    schedule 10.12.2019
comment
Я разместил его ниже. Ошибка была в инициализации приложения, где я оставил ссылки на создание Form2 и Form3. Затем, когда я создал дубликаты форм в коде, ссылки были потеряны, и он никогда не обновлял списки. Еще раз спасибо! Вы заставили меня глубоко изучить файлы .fmx, чего я никогда раньше не делал. Я искренне ценю всю вашу помощь!   -  person Ric Crooks    schedule 10.12.2019


Ответы (2)


Единственный способ воспроизвести ошибку - установить неправильное отношение родитель-потомок между формой и двумя панелями.

Например. Я могу воспроизвести описанное вами ошибочное поведение, если Panel2 является дочерним элементом Panel1, но если они оба являются дочерними элементами формы, я не могу воспроизвести проблему. Возможно, вы по ошибке уронили вторую панель на Panel1? Проверьте окно структуры в среде IDE.


Изменить после добавления в вопрос .fmx файлов

Глядя на предоставленный Unit1.fmx, который заканчивается так:

  object Panel1: TPanel
    Align = Center
    Size.Width = 640.000000000000000000
    Size.Height = 393.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
    object Layout1: TLayout
      Align = Contents
      Size.Width = 640.000000000000000000
      Size.Height = 393.000000000000000000
      Size.PlatformDefault = False
      TabOrder = 0
    end
  end

Нет Panel2 как должно быть по Unit1.pas. Вместо этого есть Layout1 как дочерний элемент Panel1. Таким образом, опубликованный файл .fmx не соответствует файлу .pas, который вы предоставили вчера. Но это все еще может подтвердить то, что я уже сказал.

Если Panel2 был заменен этим макетом, и, другими словами, Panel2 был дочерним по отношению к Panel1, то это точно объяснило бы поведение, о котором вы спрашивали изначально.

person Tom Brunberg    schedule 09.12.2019
comment
Утром проверю и сообщу! Спасибо, что изучили это! - person Ric Crooks; 10.12.2019
comment
Я только что просмотрел свой тестовый код (опубликованный выше), и обе панели являются дочерними элементами Form1. Извините, я забыл, что загрузил тестовый код в свое программное обеспечение CM. Еще раз спасибо за предложение! - person Ric Crooks; 10.12.2019
comment
Мои извинения, Том! Я начал играть с TLayout, согласно комментарию Джерри, и случайно опубликовал рабочий код вместо исходного кода. Мне очень жаль! Я обновил приведенный выше код .fmx, чтобы отразить оригинальный двухпанельный дизайн! Еще раз прошу прощения! - person Ric Crooks; 10.12.2019

Хорошо, благодаря разочарованию Тома (еще раз извините, Том!) и своему собственному, я еще раз пересобрал тестовое приложение. На этот раз ни в одном списке не отображались строки. Затем я начал думать о том, что Form2 и Form3 теряют связь с Form1, поэтому я просмотрел файлы .fmx, о которых спрашивал Том. Это привело меня к просмотру файла инициализации проекта, и вуаля!

program Project1;

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

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  //Application.CreateForm(TForm2, Form2);  <-- NOW COMMENTED OUT
  //Application.CreateForm(TForm3, Form3);  <-- NOW COMMENTED OUT
  Application.Run;
end.

Приложение создавало Form2 и Form2 при запуске, а затем я создавал дубликаты в коде, что приводило к потере ссылки. Я просто закомментировал строки Form2 и Form3 при запуске, чтобы приложение не создавало их, и оно работает, как задумано. Спасибо, Том!

person Ric Crooks    schedule 10.12.2019
comment
Что ж, это, конечно, частично объясняет проблему, но поскольку Form2 на самом деле работало так, как предполагалось, вы, должно быть, уже ранее закомментировали первую из этих строк. Таинственны пути жуков ;) - person Tom Brunberg; 10.12.2019
comment
Да, я прокомментировал это в прошлом месяце, когда впервые начал работать над этим тестовым приложением. Несколько дней назад, когда я добавил Form3, я совершенно забыл прокомментировать его. Вы совершенно правы, загадочны пути жуков! Том, я действительно благодарю вас за всю вашу помощь здесь! - person Ric Crooks; 10.12.2019