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;
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
OnClick
handler doesn't free the frame (and thus the button)RefreshFrame
TTimer
that the button enables and which calls RefreshFrame
from its OnTimer
event where the timer also is disabledThe idea being that the buttons OnClick
handler can complete before the frame and button are destroyed.