delphifiremonkeytabcontroldelphi-10-seattletframe

(Delphi FMX) How can I refresh my Frame Object inside a TabControl.TabItem without losing control of the window?


This application is intended initially for a Windows environment.

While having used only the initial code on the link mentioned below, I had to resorted to replacing the code to accept TabControls/TabItems (FMX) and not using PageControls/TabSheet (VCL) from the link below. With intent to create the Frame with the ability to rebuild content inside of a TabItem (Freeing itself and then using the Construct/Create object approach inside of a procedure).

embarcadero.com (Replacing TabSheets with Frames - by Dan Miser)

Every time I use tiframe1 while having been using Frames since Delphi Seattle 10, I have become accustomed to the ability of using them dynamically. This is one of the approaches. (problem occurs to happen at frame.Free; ) This approach causes the Application to not respond to moving the Window or Exit/Close or anything having to do with the Window layer (including a menu bar).

Does this have anything to do with the fact that TFrames were originally made for VCL?

Project1.dpr

program Project1;

uses
  System.StartUpCopy,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  frame1 in 'frame1.pas' {tiframe1: TFrame};

{$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.TreeView,
  FMX.Layouts, FMX.Controls.Presentation, FMX.StdCtrls, FMX.TabControl, FMX.Edit;

type
  TForm1 = class(TForm)
    TabControl1: TTabControl;
    TabItem1: TTabItem;
    TabItem2: TTabItem;
    procedure FormCreate(Sender: TObject);
    procedure RefreshFrame();

  private
    { Private declarations }
    procedure CreateFrame(ATabitem: TTabItem);

    function GetFrame(ATabitem: TTabItem): TFrame;
  public
    { Public declarations }

  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses frame1;

type
  TFrameClass = class of TFrame;

procedure TForm1.FormCreate(Sender: TObject);
begin
  TabItem1.Tag := Integer(Ttiframe1);
  CreateFrame(TabItem1);
end;

function TForm1.GetFrame(ATabitem: TTabItem): TFrame;
begin
  if not Assigned(ATabitem) then
    ATabitem := TabControl1.ActiveTab;
  Result := nil;
  if Assigned(ATabitem) and (ATabitem.ControlsCount > 0) and (ATabitem.Controls[0] is TFrame) then
    Result := TFrame(ATabitem.Controls[0]);
end;

procedure TForm1.CreateFrame(ATabitem: TTabItem);
var
  frame: TFrame;
begin
  if GetFrame(ATabitem) = nil then
    if ATabitem.Tag <> 0 then
    begin
      frame := TFrameClass(ATabitem.Tag).Create(Self);
      frame.Parent := ATabitem;
    end;
end;

procedure TForm1.RefreshFrame();
var
  frame: TFrame;
begin
  if Assigned(FindComponent('tiframe1')) then // 
  begin
    frame := FindComponent('tiframe1') as TFrame;
    frame.Free;  //This is the cause of all the problems
    frame := Ttiframe1.Create(Self);
    frame.Parent := TabControl1;
  end;
end;

end.

And don't forget to create a Frame and using a Construct/Create as well as at the bottom (before "end.") create an RegisterClass as well.

unit frame1;

interface

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

type
  Ttiframe1 = class(TFrame)
    Button1: TButton;

    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }

  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
  end;

implementation

{$R *.fmx}

uses Unit1;

procedure Ttiframe1.Button1Click(Sender: TObject);
begin
  Form1.RefreshFrame();
end;

constructor Ttiframe1.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

initialization
  RegisterClass(Ttiframe1);

end.

Now I am also looking to see if TabItems can be re-rendered to display anything new or updated (sort of in a Refresh or Application.ProcessMessages approach), in a similar fashion as a Constructor in a Frame could, effecting such elements as TLabel.Text or perhaps even a TEdit.Text. Inside the construct I have it fetch data from a db dynamically.

The reason for the replacement is that While using (TTreeView) inside of, Frame, inside a TabItem, or otherwise, causes similar occurrence without having a known cause to make the attention/focus away from the MainForm window when I should use the RefreshFrame;


Solution

  • I understand that this is just some made up code to demonstrate the problem, so I will not go into other oddities in the code, but will focus on the problem you describe.

    The problem is that the OnClick handler of the button frees the frame and thus the button and the handler returns to a non-existant button.

    To avoid this you can do any one of the following

    The idea being that the buttons OnClick handler can complete before the frame and button are destroyed.