如何将 TTabControl 的每个选项卡放在自己的单元中/使用 TFrame 而不是选项卡?
How to have each tab of a TTabControl in its own unit / use TFrame instead of tabs?
我有一个带有显示 10 个选项卡的 TabControl 的 FMX 应用程序(但在 VCL 中应该是相同的)。选项卡根据应用程序状态和用户权限设置为可见或不可见。
效果很好,但我不喜欢
每个人都在一起,混杂在主要形式中
并且选项卡内容会被初始化,即使它们从未变得可见。
所以我考虑使用在选项卡可见时创建的框架。
每个框架只能存在一次,应该可以很容易地从一个框架操作另一个框架(另一个框架的访问控制)。
我喜欢优雅的解决方案和简短的代码:)
这是我已经找到的,非常好,但它很旧:
Replacing TabSheets with Frames - by Dan Miser
此解决方案的每一帧都有一个全局变量。所有框架都可以在其实现部分使用 主窗体单元,并且可以轻松访问其他框架及其所有控件,甚至无需将其他框架添加到 uses 子句中。
选项卡开始时不可见,并且它们的框架未初始化。 TabA.Activate;
显示选项卡并设置焦点。 TabA.Frame.Label1
轻松访问该框架上的控件。 TabA.Visible:= False;
隐藏选项卡并释放框架。
泛型在这里很有用,我喜欢。
非常欢迎提出改进意见...
type
TFormMain = class(TForm)
TabControl: TTabControl;
TabInfo: TTabItem;
procedure FormCreate(Sender: TObject);
private
procedure AddTab<T: TTabItem>(out Tab: T);
public
TabA: TTabItemFrame<TFrameA>;
TabB: TTabItemFrame<TFrameB>;
TabC: TTabItemFrame<TFrameC>;
end;
var
FormMain: TFormMain;
implementation
procedure TFormMain.FormCreate(Sender: TObject);
begin
AddTab(TabA);
AddTab(TabB);
AddTab(TabC);
TabA.Activate;
end;
procedure TFormMain.AddTab<T>(out Tab: T);
begin
Tab:= TabControl.Add(T) as T;
end;
---------------------------------------------------------------------
unit _FrameBase;
interface
uses
System.Classes, FMX.Forms, FMX.TabControl;
type
TFrameBase = class abstract(TFrame)
public
class function GetTitle: string; virtual; abstract;
end;
TTabItemFrame<T: TFrameBase> = class(TTabItem)
private
FFrame: T;
protected
procedure Hide; override;
procedure Show; override;
public
constructor Create(AOwner: TComponent); override;
function Activate: T;
property Frame: T read FFrame;
end;
implementation
{ TTabItemFrame }
constructor TTabItemFrame<T>.Create(AOwner: TComponent);
begin
inherited;
Text:= T.GetTitle;
Visible:= False;
end;
function TTabItemFrame<T>.Activate: T;
begin
Visible:= True;
TabControl.ActiveTab:= Self;
Result:= FFrame;
end;
procedure TTabItemFrame<T>.Hide;
begin
inherited;
FFrame.DisposeOf;
FFrame:= nil;
end;
procedure TTabItemFrame<T>.Show;
begin
inherited;
FFrame:= T.Create(Self);
FFrame.Parent:= Self;
end;
end.
---------------------------------------------------------------------
type
TFrameA = class(TFrameBase)
Label1: TLabel;
public
class function GetTitle: string; override;
end;
implementation
// if it's necessary to access components or methods of
// any other frame or the main form directly
uses
_FormMain;
//...
更新:我决定在我的 FMX 应用程序中使用表单而不是框架,因为框架在设计时不能使用样式。副作用是我可以使用表单标题作为选项卡标题,而不是 class 函数。
将表单嵌入到 tabitem 中有点棘手:
constructor TFormTabBase.Create(AOwner: TComponent);
begin
inherited;
while ChildrenCount > 0 do Children[0].Parent:= AOwner as TTabItem;
end;
procedure TTabItemForm<T>.Show;
begin
inherited;
FFormTab:= T.Create(Self);
Text:= FFormTab.Caption;
end;
我有一个带有显示 10 个选项卡的 TabControl 的 FMX 应用程序(但在 VCL 中应该是相同的)。选项卡根据应用程序状态和用户权限设置为可见或不可见。
效果很好,但我不喜欢
每个人都在一起,混杂在主要形式中
并且选项卡内容会被初始化,即使它们从未变得可见。
所以我考虑使用在选项卡可见时创建的框架。
每个框架只能存在一次,应该可以很容易地从一个框架操作另一个框架(另一个框架的访问控制)。
我喜欢优雅的解决方案和简短的代码:)
这是我已经找到的,非常好,但它很旧: Replacing TabSheets with Frames - by Dan Miser
此解决方案的每一帧都有一个全局变量。所有框架都可以在其实现部分使用 主窗体单元,并且可以轻松访问其他框架及其所有控件,甚至无需将其他框架添加到 uses 子句中。
选项卡开始时不可见,并且它们的框架未初始化。 TabA.Activate;
显示选项卡并设置焦点。 TabA.Frame.Label1
轻松访问该框架上的控件。 TabA.Visible:= False;
隐藏选项卡并释放框架。
泛型在这里很有用,我喜欢。
非常欢迎提出改进意见...
type
TFormMain = class(TForm)
TabControl: TTabControl;
TabInfo: TTabItem;
procedure FormCreate(Sender: TObject);
private
procedure AddTab<T: TTabItem>(out Tab: T);
public
TabA: TTabItemFrame<TFrameA>;
TabB: TTabItemFrame<TFrameB>;
TabC: TTabItemFrame<TFrameC>;
end;
var
FormMain: TFormMain;
implementation
procedure TFormMain.FormCreate(Sender: TObject);
begin
AddTab(TabA);
AddTab(TabB);
AddTab(TabC);
TabA.Activate;
end;
procedure TFormMain.AddTab<T>(out Tab: T);
begin
Tab:= TabControl.Add(T) as T;
end;
---------------------------------------------------------------------
unit _FrameBase;
interface
uses
System.Classes, FMX.Forms, FMX.TabControl;
type
TFrameBase = class abstract(TFrame)
public
class function GetTitle: string; virtual; abstract;
end;
TTabItemFrame<T: TFrameBase> = class(TTabItem)
private
FFrame: T;
protected
procedure Hide; override;
procedure Show; override;
public
constructor Create(AOwner: TComponent); override;
function Activate: T;
property Frame: T read FFrame;
end;
implementation
{ TTabItemFrame }
constructor TTabItemFrame<T>.Create(AOwner: TComponent);
begin
inherited;
Text:= T.GetTitle;
Visible:= False;
end;
function TTabItemFrame<T>.Activate: T;
begin
Visible:= True;
TabControl.ActiveTab:= Self;
Result:= FFrame;
end;
procedure TTabItemFrame<T>.Hide;
begin
inherited;
FFrame.DisposeOf;
FFrame:= nil;
end;
procedure TTabItemFrame<T>.Show;
begin
inherited;
FFrame:= T.Create(Self);
FFrame.Parent:= Self;
end;
end.
---------------------------------------------------------------------
type
TFrameA = class(TFrameBase)
Label1: TLabel;
public
class function GetTitle: string; override;
end;
implementation
// if it's necessary to access components or methods of
// any other frame or the main form directly
uses
_FormMain;
//...
更新:我决定在我的 FMX 应用程序中使用表单而不是框架,因为框架在设计时不能使用样式。副作用是我可以使用表单标题作为选项卡标题,而不是 class 函数。
将表单嵌入到 tabitem 中有点棘手:
constructor TFormTabBase.Create(AOwner: TComponent);
begin
inherited;
while ChildrenCount > 0 do Children[0].Parent:= AOwner as TTabItem;
end;
procedure TTabItemForm<T>.Show;
begin
inherited;
FFormTab:= T.Create(Self);
Text:= FFormTab.Caption;
end;