Lifecycle & Ownership
A subject goes through a simple life: it is created (or adopted), shown, hidden, possibly shown again, and finally closed. The Status property of the info tells you where it is.
New / Use ──► Ready ──Show──► Showing ──► Visible ──Hide──► Hiding ──► Hidden ──Close──► (freed)
▲ │
└───────────────Show──────────────────┘Creating: New and Use
function New<T: TFrame>(const AParent: TFmxObject = nil; const AStandStyleName: string = ''): TFrameInfo<T>;
function Use<T: TFrame>(const AFrame: T; const AParent: TFmxObject = nil; const AStandStyleName: string = ''): TFrameInfo<T>;Both:
- resolve the parent and the stand name (see Core Concepts);
- let Responsive definitions and the
OnGetSubjectClassevent change frame class, stand and parent (Newonly); - clone the stand, add it to the parent, invisible, and put the subject in its container;
- collect the methods marked with
[BeforeShow],[Show],[AfterShow],[Hide]; - bind the Common Actions to the controls of the stand and of the subject;
- inject the fields marked with context attributes such as
[FrameInfo]or[Parent](see Context Injection).
New<T> creates the frame itself (with Owner = nil and an empty Name, so you can create many instances of the same frame) and owns it. Use<T> adopts a frame you already have, for instance one placed on the form at design time, and does not own it.
Non-generic overloads exist for frames: Use(AFrame: TFrame; ...) and New(AFrameClassName: string; ...). The latter looks the class up through RTTI by its qualified name ('Frames.Details.TDetailsFrame'), so the frame class must be linked in the executable.
Showing: Show
Show runs these steps, synchronously:
Status := Showing;- the frame's
[BeforeShow]methods, then the component'sOnBeforeShowevent; - the frame's
[Show]methods if any, otherwise the default:Stand.Visible := TrueandStand.BringToFront; - the animations of the stand that match
AnimationShow(OnShow*) are started; Status := Visible;- the frame's
[AfterShow]methods, then theOnAfterShowevent.
[AfterShow] and OnAfterShow run as soon as the animations are started, not when they end.
NewAndShow<T> combines New, an optional configuration of the frame and of the info, and Show:
FrameStand1.NewAndShow<TDetailsFrame>(Layout1, 'lightbox',
procedure (AFrame: TDetailsFrame) begin AFrame.Customer := LCustomer; end,
procedure (AInfo: TFrameInfo<TDetailsFrame>) begin AInfo.Stand.Opacity := 0.9; end);Hiding: Hide
function Hide(const ADelay: Integer = 0; const AThen: TProc = nil): Boolean;- if the subject is already hiding, no new hide starts and
HidereturnsFalse; itsAThenis called when the hide in progress completes; - the
OnBeforeHideevent;Status := Hiding; - the animations matching
AnimationHide(OnHide*) are started, and the longestDelay + Durationamong them becomes the hide delay; - after the delay (or after
ADelaymilliseconds, when you pass a value other than 0): the frame's[Hide]methods if any, otherwise the default (stop the animations,Stand.Visible := False);Status := Hidden;AThenis called; theOnAfterHideevent.
A hidden subject keeps its frame and stand, and can be shown again with Show. This is the cheapest way to reuse a frame that appears often (a side menu, a wait screen).
Closing: Close and HideAndClose
Close removes the subject from the component immediately, without animations: the stand is removed from its parent and freed, and the frame is
- freed, if it is owned (created by
New, orFrameIsOwned := True); - removed from the container and left to you, otherwise (adopted with
Use).
The info object itself is freed by Close: do not use it afterwards, and clear any field that references it.
procedure TMainForm.CloseDetails;
begin
if Assigned(FDetailsInfo) then
begin
FDetailsInfo.Close;
FDetailsInfo := nil; // the info has been freed
end;
end;HideAndClose(ADeferExecutionMS = 0; AThen = nil) is the usual way to dismiss a subject: it hides it (with its animations), waits DefaultHideAndCloseDeferTimeMS milliseconds (100 by default, or ADeferExecutionMS), then closes it and calls AThen.
It is safe to call it at any moment: if a Hide is already in progress, the subject is closed when that hide completes; if a HideAndClose is already in progress (a double tap on a close button, HideAndCloseAll while a subject is closing), the subject is closed once and every AThen is called after the close.
Closing many subjects
The component offers bulk operations, all accepting a class or an array of classes (descendants included):
FrameStand1.CloseAll; // everything, immediately
FrameStand1.CloseAll([TDetailsFrame]); // only the details frames
FrameStand1.CloseAllExcept(TMenuFrame); // all but the menu
FrameStand1.HideAndCloseAll([TDetailsFrame, TEditFrame]);
FrameStand1.HideAndCloseAllExcept([TMenuFrame]);When the component is destroyed, all the remaining subjects are closed: stands removed from their parents and freed, owned subjects freed, adopted ones detached and left to you. This holds also when the component lives elsewhere than the parents of its stands (on a data module, or on another form).
When the parent or the subject is destroyed
The stand is a child of its parent, so FMX destroys it, with the subject inside, when the parent (or the whole form) is destroyed. The component is notified (through FreeNotification) and simply forgets the subject: the info is freed, the subject leaves FrameInfos and VisibleFrames, and nothing already destroyed is touched again. The same happens when your code frees an adopted frame or form that is on a stand.
- Freeing a form with subjects still on screen is safe, in any order with respect to the component.
- A frame adopted with
Useis a child of the stand while it is shown: if its parent is destroyed, FMX destroys the frame too. Close it first (Close,CloseAll) if you want to keep it. - The controls of a form adopted by
TFormStandlive in the stand while it is shown: they share the same fate. A form created withNewis freed with its owner component.
Timing and threads
Hide delays use the FMX platform timer (TDelayedAction, no background threads): every callback ([Hide] methods, AThen, OnAfterHide, the close of HideAndClose) runs in the main thread, after the delay.
The pending callbacks belong to the info: when the subject is closed, or the component is destroyed with its form, before the delay has elapsed, they are cancelled. In particular:
Closeduring aHide(or aHideAndClose) cancels it: itsAThenandOnAfterHideare not called;- if
AThen(or a[Hide]method) closes the subject,OnAfterHideis not fired, since the info no longer exists.
Callbacks still pending when the application terminates are dropped.
All the methods of the components must be called from the main thread. See Background Work for the pattern to use with tasks.
