(Delphi FMX) 如何在不失去对 window 的控制的情况下刷新 TabControl.TabItem 中的框架对象?
(Delphi FMX) How can I refresh my Frame Object inside a TabControl.TabItem without losing control of the window?
此应用程序最初旨在用于 Windows 环境。
虽然只使用了下面提到的 link 的初始代码,但我不得不求助于替换代码以接受 TabControls/TabItems (FMX) 而不是使用 PageControls/TabSheet (VCL ) 来自下面的 link。旨在创建具有在 TabItem 中重建内容的能力的框架(释放自身,然后在过程中使用 Construct/Create 对象方法)。
embarcadero.com (Replacing TabSheets with Frames - by Dan Miser)
自从 Delphi Seattle 10 开始使用 Frames 以来,每次我使用 tiframe1 时,我已经习惯了动态使用它们的能力。这是其中一种方法。 (问题发生在 frame.Free;)这种方法导致应用程序不响应移动 Window 或 Exit/Close 或与 Window 层有关的任何事情(包括一个菜单栏)。
这与 TFrame 最初是为 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.
并且不要忘记创建一个框架并使用 Construct/Create 以及在底部(在 "end." 之前)创建一个 RegisterClass。
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.
现在我也在寻找是否可以重新呈现 TabItems 以显示任何新的或更新的内容(类似于刷新或 Application.ProcessMessages 方法),其方式与框架中的构造函数类似可以,影响 TLabel.Text 甚至 TEdit.Text 等元素。在构造内部,我让它动态地从数据库中获取数据。
替换的原因是,在 Frame 内部,TabItem 内部或其他地方使用 (TTreeView) 时,会导致类似的情况发生,而没有已知原因使 attention/focus 远离 MainForm window 当我应该使用 RefreshFrame 时;
我知道这只是一些用来演示问题的编造代码,所以我不会深入研究代码中的其他奇怪之处,但会专注于您描述的问题。
问题是按钮的 OnClick
处理程序释放了框架,因此按钮和处理程序 returns 变成了 non-existant 按钮。
要避免这种情况,您可以执行以下任一操作
- 重新设计,以便
OnClick
处理程序不会释放框架(以及按钮)
- 使用您自己定义的 Windows 消息(因为平台是 Windows),该按钮发布到表单,当表单收到时调用
RefreshFrame
- 使用按钮启用的 1 毫秒
TTimer
并从其 OnTimer
事件调用 RefreshFrame
,其中定时器也被禁用
想法是按钮 OnClick
处理程序可以在框架和按钮被销毁之前完成。
此应用程序最初旨在用于 Windows 环境。
虽然只使用了下面提到的 link 的初始代码,但我不得不求助于替换代码以接受 TabControls/TabItems (FMX) 而不是使用 PageControls/TabSheet (VCL ) 来自下面的 link。旨在创建具有在 TabItem 中重建内容的能力的框架(释放自身,然后在过程中使用 Construct/Create 对象方法)。
embarcadero.com (Replacing TabSheets with Frames - by Dan Miser)
自从 Delphi Seattle 10 开始使用 Frames 以来,每次我使用 tiframe1 时,我已经习惯了动态使用它们的能力。这是其中一种方法。 (问题发生在 frame.Free;)这种方法导致应用程序不响应移动 Window 或 Exit/Close 或与 Window 层有关的任何事情(包括一个菜单栏)。
这与 TFrame 最初是为 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.
并且不要忘记创建一个框架并使用 Construct/Create 以及在底部(在 "end." 之前)创建一个 RegisterClass。
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.
现在我也在寻找是否可以重新呈现 TabItems 以显示任何新的或更新的内容(类似于刷新或 Application.ProcessMessages 方法),其方式与框架中的构造函数类似可以,影响 TLabel.Text 甚至 TEdit.Text 等元素。在构造内部,我让它动态地从数据库中获取数据。
替换的原因是,在 Frame 内部,TabItem 内部或其他地方使用 (TTreeView) 时,会导致类似的情况发生,而没有已知原因使 attention/focus 远离 MainForm window 当我应该使用 RefreshFrame 时;
我知道这只是一些用来演示问题的编造代码,所以我不会深入研究代码中的其他奇怪之处,但会专注于您描述的问题。
问题是按钮的 OnClick
处理程序释放了框架,因此按钮和处理程序 returns 变成了 non-existant 按钮。
要避免这种情况,您可以执行以下任一操作
- 重新设计,以便
OnClick
处理程序不会释放框架(以及按钮) - 使用您自己定义的 Windows 消息(因为平台是 Windows),该按钮发布到表单,当表单收到时调用
RefreshFrame
- 使用按钮启用的 1 毫秒
TTimer
并从其OnTimer
事件调用RefreshFrame
,其中定时器也被禁用
想法是按钮 OnClick
处理程序可以在框架和按钮被销毁之前完成。