This solution has a global variable for each frame. All frames can use the main form unit in their implementation part and have easy access to other frames and all their controls, even without adding the other frames to the uses clause.
Tabs start invisible and their frame is uninitialized. TabA.Activate;
shows the tab and sets focus. TabA.Frame.Label1
easily accesses a control on that Frame. TabA.Visible:= False;
hides the tab and frees the frame.
Generics are really helpful here, I like it.
Ideas for improvements are very welcome...
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;
//...
Update: I decided to use forms instead of frames for my FMX application because frames cannot use styles at design time. A side effect is that instead of the class function I can use the form caption for the tab title.
Embedding a form into a tabitem is a little tricky:
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;