Search code examples
delphivcldelphi-10-seattle

Error Creating TActivityIndicator at Runtime


I saw the introduction of the TActivityIndicator in Delphi 10 Seattle and thought cool I could make use of this somewhere. I wanted to use this to show that some dynamically created sections of my form were still loading the data before populating the form. So I thought I'd do this before I start loading my data in FormShow, where self is the form.

indicator := TActivityIndicator.Create(self);
indicator.IndicatorSize := TActivityIndicatorSize.aisLarge;

Sadly when I try to create them dynamically and set then TActivityIndicator.IndicatorSize I get an exception ... EInvalidOperation with message 'Control '<name>' has no parent window' Which stepping through the VCL takes me to Vcl.Controls TWinControl.CreateWnd specifically

if (WndParent = 0) and (Style and WS_CHILD <> 0) then
  if (Owner <> nil) and (csReading in Owner.ComponentState) and
    (Owner is TWinControl) then
    WndParent := TWinControl(Owner).Handle
  else
    raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);

I've checked Owner is the form which is of course a TWinControl but (csReading in Owner.ComponentState) returns false. Stepping through Owner.ComponentState = [] on FormCreate and [csFreeNotification] on FormShow.

I've found that if you try to change the IndicatorSize of a TActivityIndicator that was created at design time then it works perfectly. So what am I missing here or is it not possible to create TActivityIndicators at runtime?


Solution

  • The error message is pretty clear. You need to assign a Parent on which the activity indicator will draw itself. The Owner is the component responsible for freeing the control when the Owner is destroyed; the Parent is the control on which the control will be drawn (parented) for display.

    The solution is to assign that parent in code:

    Indicator := TActivityIndicator.Create(Self);
    Indicator.Parent := Self; // <-- here
    // Set any other properties here
    

    The same issue is common on all visual controls (such as TEdit, TLabel, TMemo, and so forth), which all need to have a Parent assigned in order to have a place to paint themselves. And in some cases, a Parent is required in order for various properties in the child control to function correctly when they depend on the child having an HWND window, which requires a Parent window, and so on.

    If I understand your intent, I think you're going to be disappointed, however. TActivityIndicator is pretty static; it's not threaded, which means it will cease updating if your form is busy and doesn't process timer messages (which it uses internally).