How can I set/get property value through RTTI for complex things like TStringGrid.Cells?
Asked Answered
J

1

7

I have values stored in xml and lua code and accessing object's properties through RTTI.

var
  o, v: TValue; // o is current object
  a: TStringDynArray; // params as array
  ctx: TRttiContext;
  tt: TRttiType;
  p: TRttiProperty;
  pt: PTypeInfo;
begin
...
  ctx := TRttiContext.Create;
  try
    o := GetLastClassInParams(ctx, obj, a, param_idx);
    tt := ctx.GetType(o.TypeInfo);
    if high(a) < param_idx then
        raise Exception.Create(S_FN + S_NOP);
    p := tt.GetProperty(a[param_idx]);
    if p = nil then
        raise Exception.Create(S_FN + S_PNE + a[param_idx]);
    pt := p.PropertyType.Handle;
    case p.PropertyType.TypeKind of
      tkInteger: v := TValue.From<integer>(integer(Value));
      tkEnumeration: v := TValue.FromOrdinal(pt, GetEnumValue(pt, VarToStr(Value)));
      tkUString: v := TValue.From<string>(VarToStr(Value));
      tkFloat: v := TValue.From<double>(double(Value));
      tkSet: begin
          temp_int := StringToSet(pt, VarToStr(Value));
          TValue.Make(@temp_int, pt, v);
        end;
    else v := TValue.FromVariant(Value);
    end;
    p.SetValue(o.AsObject, v);

I can work with many properties like Width, Lines.Text of TMemo etc, even with Panels[0].Width of TStatusBar (where Panels is TCollection descendant), but thing like TStringGrid.Cells[x, y] is something I can't solve. There is help on Embarcadero and some functions like GetIndexedProperty (maybe that is what I need), but explanation there as good as "Gets Indexed Property".

How to set and get TStringGrid.Cells[x,y] through RTTI at runtime if I have values stored as strings like "Cells[1,1]"?

Josphinejoss answered 29/3, 2013 at 11:59 Comment(0)
C
6

Here's the simplest example I can think off to get and set the values from a string grid using RTTI:

var
  ctx: TRttiContext;
  rttitype: TRttiType;
  rttiprop: TRttiIndexedProperty;
  value: TValue;
....
rttitype := ctx.GetType(StringGrid1.ClassType);
rttiprop := rttitype.GetIndexedProperty('Cells');
value := rttiprop.GetValue(StringGrid1, [1, 1]);
rttiprop.SetValue(StringGrid1, [1, 1], value.ToString + ' hello');

I excised the error checking for the sake of simplicity. I'm going to assume that you already know how to check for errors.

Chaldron answered 29/3, 2013 at 12:16 Comment(7)
Thanks. Is there any way to find when I should use GetIndexedProperty and when GetProperty?Josphinejoss
OK, I'm guessing that you are parsing text like this: Cells[1,1] := .... In which case the presence of [] tells you that it is an indexed property. You can also use TRttiType.GetIndexedProperties and check if your property is in that list.Chaldron
Yes, but was curious is Delphi knows that property is indexed and maybe RTTI can return somehow IsIndexed. Thanks.Josphinejoss
I looked but could not find such a propertyChaldron
Because there is problem: TStatusBar.Panels[x] is property and nil on attempt to read as GetIndexedProperty('Panels') while TStringGrid.Cells[x,y] is indexed property and nil on GetProperty('Cells'). I can't just get indexed and not indexed properties into arrays to search because they have different types. And array values is exact types used by different functions. Or - I can, but it have no sense. Only option is to get them both and decide by nil or not.Josphinejoss
That's a sensible approach. Try both and whichever is non-nil is your guy.Chaldron
So far it works by non-nil and result := TCollection(myObjAsTValue.AsObject).Items[x] for ordinal properties where myObjAsTValue.AsObject is TCollection.Josphinejoss

© 2022 - 2024 — McMap. All rights reserved.