Delphi – Get/Set sub properties ussing RTTI

delphidelphi-xertti

Given the following code snippet below, using GetPropValue(MyComponent,'MySubComponent.Prop1') raises an EPropertyError exception.
How can I retrieve or set the values of SubProperties using GetPropValue / SetPropValue?

Type
  TMySubComponent = class(TInterfacedPersitent)
  private
    FProp1: Integer;
  published
    property Prop1: integer read FProp1 write FProp1;
  end;

  TMyComponent = class(TCompoent)
  private
    FMySubComponent : TMySubcomponent; 
  published
    property MySubComponent: TMySubComponent read FMySubComponent write FMySubComponent ;
  end;

Best Answer

As Robert says the dot notation is not supported , but you can create easily a function to set or get a sub-property value using the RTTI. check this sample

{$APPTYPE CONSOLE}

uses
  Rtti,
  Classes,
  SysUtils;


Type
  TMySubComponent = class(TInterfacedPersistent)
  private
    FProp1: Integer;
  published
    property Prop1: integer read FProp1 write FProp1;
  end;

  TMyComponent = class(TComponent)
  private
    FMySubComponent : TMySubcomponent;
  published
    property MySubComponent: TMySubComponent read FMySubComponent write FMySubComponent ;
  end;



procedure SetObjValueEx(const ObjPath:string;AInstance:TObject;AValue:TValue);
Var
 c            : TRttiContext;
 Prop         : string;
 SubProp      : string;
 pm           : TRttiProperty;
 p            : TRttiProperty;
 Obj          : TObject;
begin
 Prop:=Copy(ObjPath,1,Pos('.',ObjPath)-1);
 SubProp:=Copy(ObjPath,Pos('.',ObjPath)+1);
 c := TRttiContext.Create;
 try
   for pm in c.GetType(AInstance.ClassInfo).GetProperties do
   if CompareText(Prop,pm.Name)=0 then
   begin
     p := c.GetType(pm.PropertyType.Handle).GetProperty(SubProp);
      if Assigned(p) then
      begin
        Obj:=pm.GetValue(AInstance).AsObject;
        if Assigned(Obj) then
          p.SetValue(Obj,AValue);
      end;
      break;
   end;
 finally
   c.Free;
 end;
end;


function GetObjValueEx(const ObjPath:string;AInstance:TObject):TValue;
Var
 c            : TRttiContext;
 Prop         : string;
 SubProp      : string;
 pm           : TRttiProperty;
 p            : TRttiProperty;
 Obj          : TObject;
begin
 Prop:=Copy(ObjPath,1,Pos('.',ObjPath)-1);
 SubProp:=Copy(ObjPath,Pos('.',ObjPath)+1);
 c := TRttiContext.Create;
 try
   for pm in c.GetType(AInstance.ClassInfo).GetProperties do
   if CompareText(Prop,pm.Name)=0 then
   begin
     p := c.GetType(pm.PropertyType.Handle).GetProperty(SubProp);
      if Assigned(p) then
      begin
        Obj:=pm.GetValue(AInstance).AsObject;
        if Assigned(Obj) then
          Result:=p.GetValue(Obj);
      end;
      break;
   end;
 finally
   c.Free;
 end;
end;

Var
  MyComp : TMyComponent;
begin
  try
     MyComp:=TMyComponent.Create(nil);
     try
       MyComp.MySubComponent:=TMySubComponent.Create;
       //Set the value of the property 
       SetObjValueEx('MySubComponent.Prop1',MyComp,777);
       //Get the value of the property 
       Writeln(Format('The value of MySubComponent.Prop1 is %d',[GetObjValueEx('MySubComponent.Prop1',MyComp).AsInteger]));
     finally
       MyComp.Free;
     end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
Related Topic