c++formsfiremonkeyc++builder-xe8

Firemonkey: Getting parent form of control


I have a custom control which needs to get access to the height of the main form it is on. Since it is common for this control to be nested in a series of panels, I have written this code to try and get me to the main form:

TControl * control = this;

while( control->HasParent() )
{
    control = control->ParentControl;
    ShowMessage( control->Name );
}

Using the ShowMessage statement to track my progress, as I step through the code I get all the way up to "BasePanel" which in this case is the last control up the ladder before the "MainForm." However, when the call to ShowMessage happens for what should be the "MainForm" I get an access violation.

Is there some reason I am unable to access the main form of a control this way? Is there a better way to access a control's main form?


Solution

  • You are not checking if ParentControl returns a NULL pointer before reading its Name. When HasParent() returns true, ParentControl is NOT guaranteed to be valid. Case in point - TForm is NOT a TControl descendant in FireMonkey, so it cannot be returned by ParentControl.

    The purpose of HasParent() is to report whether the component has a parent or not. TFmxObject overrides HasParent() to report whether the TFmxObject.Parent property is NULL, and overrides GetParentComponent() to return an appropriate TComponent for that parent. TFmxObject.Parent returns a TFmxObject, as parent/child relationships do not have to be visual in FireMonkey like they do in VCL, so Parent and GetParentComponent() can actually return different objects at times.

    You should be using GetParentComponent() instead of ParentControl, as the documentation says:

    Call HasParent to determine whether a specific component has a parent.

    Derived classes override this method to implement proper handling for parenting.

    Use GetParentComponent to retrieve the component reference.

    For example:

    TComponent * comp = this;
    
    while( comp->HasParent() )
    {
        comp = comp->GetParentComponent();
        ShowMessage( comp->Name );
    }
    

    However, if your intent is to find the parent TForm specifically, use your control's Root property instead:

    TCommonCustomForm *form = dynamic_cast<TCommonCustomForm*>(this->Root->GetObject());