c++c++builderfiremonkeyc++builder-10.1-berlin

When exactly are published properties assigned at run time?


I have a custom control which needs to do some things when loading at run time, based on a published property. However, I am running into a problem where, whenever I check the published property, it has not been set yet, and is always the default value.

I first attempted checking the property in the constructor, of the control, but quickly found they were not loaded yet. I know that when the control is shown on the screen the properties are set correctly, thus its not an issue with the properties not being loaded at all.

I next attempted overriding the Loaded Method but am still having the same problem, so I don't think this is exactly what I am looking for.

void __fastcall TFmSearchBar::Loaded()
{
    TEdit::Loaded(); //call base class loaded

    if( MyProperty )
    {
        //do stuff
    }
}   

At what point are these published properties actually getting set?

What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?


Solution

  • If I check the property in the constructor of the control, the property is always the default value even if I have specified otherwise in the designer.

    Correct, because its design-time values have not been assigned yet.

    At what point are these published properties actually getting set?

    When the Owner (Form, Frame, or DataModule) is being constructed. It loads its own DFM resource and parses it, constructing stored child components and reading their property values.

    For example, say you have the following DFM:

    object Form1: TForm1
      Left = 0
      Top = 0
      Caption = 'Form1'
      ...
      object Edit1: TEdit
        Left = 136
        Top = 64
        Width = 121
        Height = 21
        TabOrder = 0
      end
      object Button1: TButton
        Left = 263
        Top = 62
        Width = 75
        Height = 25
        Caption = 'Button1'
        TabOrder = 1
      end
    end
    

    The DFM streaming process roughly translates to the following equivalent code (I'm leaving a lot of internal details out for simplicity):

    __fastcall TCustomForm::TCustomForm(TComponent *Owner)
        : TScrollingWinControl(Owner)
    {
        this->FFormState << fsCreating;
        try
        {
            // locate, load, and parse the "Form1" DFM resource ...
    
            this->FComponentState << csLoading;
            this->Parent = ...;
            this->Name = L"Form1":
            this->FComponentState << csReading;
            this->Left = 0;
            this->Top = 0;
            this->Caption = L"Form1";
            ...
    
            TEdit *e = new TEdit(this);
            try
            {
                e->FComponentState << csLoading;
                e->Parent = this;
                e->Name = L"Edit1"; // <-- sets the derived Form's 'Edit1' member to this object
                e->FComponentState << csReading;
                e->Left = 136;
                e->Top = 64;
                e->Width = 121;
                e->Height = 21;
                e->TabOrder = 0;
                e->FComponentState >> csReading;
            }
            catch (...)
            {
                delete e;
                throw;
            }
    
            TButton *b = new TButton(this);
            try
            {
                b->FComponentState << csLoading;
                b->Parent = this;
                b->Name = L"Button1"; // <-- sets the derived Form's 'Button1' member to this object
                b->FComponentState << csReading;
                b->Left = 263;
                b->Top = 62;
                b->Width = 75;
                b->Height = 25;
                b->Caption = L"Button1";
                b->TabOrder = 1;
                b->FComponentState >> csReading;
            }
            catch (...)
            {
                delete b;
                throw;
            }
    
            this->FComponentState >> csReading;
    
            ...
    
            e->Loaded();
            b->Loaded();
            this->Loaded();
        }
        __finally
        {
            this->FFormState >> fsCreating;
        }
    }
    

    So, as you can see, a component's property values are not available yet when its constructor is called.

    What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?

    That depends on what the properties need to do. If they need to perform operations immediately, you can do that directly in their property setters. But if they need to wait until other properties have been loaded first (if one property is dependent on the value of another property), then override the virtual Loaded() method instead, which is automatically called after DFM streaming is finished. Property setters can check the flags of the ComponentState property to know whether or not the component is currently running in the Form Designer at design-time, whether or not a DFM is currently being streamed, etc and then act accordingly as needed.

    I attempted overriding the Loaded Method but am still having the same problem

    Which is what exactly? You did not explain what your actual problem is. Please edit your question to provide those details.

    so I don't think this is exactly what I am looking for.

    It most likely is, you probably are just not using it correctly.