Search code examples
delphipropertiesrtti

TControl (child) to TObject and set its Parent property via RTTI or SetXXXXXXProperty


Currently doing some research on RTTI in delphi.

The problem that arises is the following.

For this example I have a TPanel component (created from code and the owner is passed by the invoke method) Now the TPanel is cast to a TObject (to keep the methods I use generic).

Now I try to set that property via the SetXXXXXXProp function (XXXXXX is any of the following (ord, variant, str, method.... basically I tried all of the SetProperty functions.)

But for some odd reason the message I receive is that there is no Parent property. How can I set my Parent property??

So I hope any of you can give me a 'Pointer' in the right direction.

class procedure TComponentsCreator.AddParent(obj, parent : TObject);
var
  count : integer;
  propList: PPropList;
  I: Integer;

  method : TMethod;
begin

  count := GetPropList(obj, propList);

  for I := 0 to count -1 do
    begin

      WriteLn(propList[i]^.Name);

      if (CompareStr(LowerCase(String(propList[i]^.Name)), LowerCase('Parent')) = 0) then
        begin
          SetObjectProp(obj, String(propList[i]^.Name), parent);
        end;
    end;
end;    

Solution

  • You are using the old style RTTI which was designed for the .dfm streaming mechanism. When you call GetPropList the list returned contains the published properties. You are looking for Parent which is public but not published.

    You could use the new style RTTI to achieve this but that would seem to be unnecessary. RTTI is needed when the member or type that you wish to operate on is not known at compile time. However, you do know the member. It is TControl.Parent. So you can write

    (obj as TControl).Parent := parent;
    

    If for some reason, you felt compelled to use RTTI, then the simplest way to write the code that I can find is:

    procedure SetControlParent(obj, parent: TObject);
    var
      ctx: TRttiContext;
      typ: TRttiType;
      prop: TRttiProperty;
    begin
      typ := ctx.GetType(obj.ClassType);
      prop := typ.GetProperty('Parent');
      prop.SetValue(obj, parent);
    end;
    

    I've omitted any error checking here, but I expect that you would not do the same in real code.


    As a minor aside, your call to CompareStr is needlessly complex. If you wish to use case insensitive comparison use CompareText. If you wish to compare for equality use SameText. You could replace that code more clearly and simply like this:

    if SameText(propList[I]^.Name, 'Parent') then