3
votes

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;
2
Which version of delphi are you using?RRUZ
is the property IsSubComponent set to True in your TMySubComponent class ?az01

2 Answers

7
votes

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.
4
votes

The dot notation you used in your question is not supported.

You need to get the Value of the SubComponent, then perform the Set and Get on the individual properties.

var
  C: TRttiContext;
  MyComp : TMyComponent;
  MyCompType : TRttiInstanceType;
  MySubCompType : TRttiInstanceType;
  MySubComponentValue : TValue;
begin
  MyComp := TMyComponent.create(Self); 
  ...
  // RTTI.Pas Method
  MyCompType :=  c.GetType(TMyComponent.ClassInfo) as TRttiInstanceType;
  MySubCompType := c.GetType(TMySubComponent.ClassInfo) as TRttiInstanceType;
  MySubComponentValue := MyCompType.GetProperty('MySubComponent').GetValue(MyComp);

  if Not MySubComponentValue.IsEmpty then
  begin
      MySubCompType.GetProperty('Prop1').SetValue(MySubComponentValue.AsObject,43);
  end;

 //TypInfo.pas Method
 SubComp := GetObjectProp(MyComp,'MySubComponent');
 if Assigned(SubComp) then
 begin
    SetPropValue(SubComp,'Prop1',5);
    prop1Value := GetPropValue(SubComp,'Prop1');
 end;

end;

The TypInfo.pas method will only work with published properties, you can get the public properties with the RTTI.pas method.