WhenReady: Scheduling to-do's
This is where our lifecycle gets clever. What if your component's constructor needs to create other child widgets or set some property the moment the widget reaches ready state? Wouldnt it be nice to be able to tell the widget "hey, do this immediately after you are done constructing, ok?".
This is a very real scenario. If you create a new TQTXLabel inside your constructor and immediately try to alter something that requires the label to be fully realized, it will fail because that new label is still being constructed.
WhenReady() is the solution for this. It is a tool that both widget creators and general users can make clever use of. You use it to schedule a procedure to run as soon as the widget enters its wsReady state (which is generally when the constructor exits).
When you call WhenReady(MyProcedure), one of two things happens:
- If the widget is still wsCreating, your procedure is added to a internal stack the widget maintains
- When ObjectReady() is called, it executes the procedures on that stack in the same sequence as you added them.
- It also works on child widgets inside that "weird" constructor callback! (yes, it was well thought out, it just takes time to get used to it)
This pattern is used extensively in the RTL. A perfect example is TQTXCustomListbox.AddText. This method creates a TQTXListItemText and needs to set its Caption and alignment properties. It uses WhenReady to safely schedule this setup.
// Inside TQTXCustomListbox.AddText
function TQTXCustomListbox.AddText(Caption: string): TQTXListItemText;
begin
// Create the new child widget
result := TQTXListItemText.Create(self, procedure (ListItem: TQTXListItem)
begin
// The item is still "wsCreating" here.
// We schedule the setup code using WhenReady.
ListItem.WhenReady( procedure (Widget: TQTXWidget)
begin
// This code runs when the ListItem is ready.
var lItem := TQTXListItemText(Widget);
lItem.AlignH := chLeft;
lItem.AlignV := cvMiddle;
lItem.Caption := Caption;
end);
end);
end;