GetPropList with TRectangle returns StrokeThickness as 属性 这应该是 Stroke class 的一部分

GetPropList with TRectangle returns StrokeThickness as property which should be part of Stroke class

我正在使用 Delphi Seattle Update1 Win64 并尝试使用 RTTI 提取属性。我的objective是将组件属性序列化为JSON,因为我需要在非Delphi环境中使用这些信息。

我的问题是关于 TRectangle(示例)的 GetPropList 以及为什么它 return 的属性无法传递给 GetPropValue,即:

  1. StrokeThickness 作为类型 tkFloat
  2. StrokeCap 作为类型 tkEnumeration
  3. StrokeDash 作为类型 tkEnumeration
  4. StrokeJoin 作为 tkEnumeration 类型。

GetPropList 正确地 returns Stroke 作为类型 tkClass,这是我所期望的,并且在解析时,Stroke class return ThicknessCapDashJoin 我可以从中得到正确的值。

问题是在 StrokeThickness 上执行 GetPropValue 会导致异常。因此,我必须对由 GetPropList 编辑的 "broken" 属性 return 进行特殊处理,我想避免这种情况。

起初我认为这是 GetPropList returning 一个不存在的 属性 的问题,但我可以在代码中执行以下代码并且它们都有效:

   Rectangle1.StrokeThickness := 5;   //works

   Rectangle1.Stroke.Thickness := 10; //and also works

tkFloat 或 tkEnumeration 类型的其他属性按预期工作并且 return 正确的值。

我创建了一个小型测试应用程序来尝试对此进行调试。我发现在 StrokeThickness 的情况下,M.Code 在函数 System.TypeInfo.TPropSet.GetProp(第 2397 行)中为 nil,我想这解释了为什么会导致异常。

附件是我创建的测试代码,用于确认我在更大的项目中看到的内容。如果没有特殊情况,我将如何处理上面列出的四个属性。

表格:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 202
  ClientWidth = 542
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  DesignerMasterStyle = 0
  object Rectangle1: TRectangle
    Position.X = 40.000000000000000000
    Position.Y = 40.000000000000000000
    Size.Width = 97.000000000000000000
    Size.Height = 97.000000000000000000
    Size.PlatformDefault = False
  end
  object StrokeThickness: TButton
    Position.X = 40.000000000000000000
    Position.Y = 144.000000000000000000
    Size.Width = 97.000000000000000000
    Size.Height = 22.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 1
    Text = 'RTTI'
    OnClick = StrokeThicknessClick
  end
  object Memo1: TMemo
    Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
    DataDetectorTypes = []
    Position.X = 152.000000000000000000
    Position.Y = 40.000000000000000000
    Size.Width = 353.000000000000000000
    Size.Height = 129.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 2
    Viewport.Width = 349.000000000000000000
    Viewport.Height = 125.000000000000000000
  end
end

测试代码:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, FMX.Edit, FMX.Objects, FMX.ScrollBox, FMX.Memo;

type
  TForm1 = class(TForm)
    Rectangle1: TRectangle;
    StrokeThickness: TButton;
    Memo1: TMemo;
    procedure StrokeThicknessClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses System.TypInfo;

{$R *.fmx}

procedure TForm1.StrokeThicknessClick(Sender: TObject);
var
  vValue : String;
  PropList : PPropList;
  PropInfo : PPropInfo;
  PropType : PPTypeInfo;
  PropListCount : Integer;
  I: Integer;
begin
   memo1.Lines.Clear;

   PropListCount := GetPropList(Rectangle1, PropList);

   for I := 0 to PropListCount-1 do
   begin
     PropInfo := PropList^[I];
     PropType := PropInfo^.PropType;

     Memo1.Lines.Add('Name: '+String(PropInfo^.Name) );
     Memo1.Lines.Add('PropType: '+String(PropInfo^.PropType^.Name) );
     Memo1.Lines.Add('PropKind: '+GetEnumName(TypeInfo(TTypeKind), Ord(PropType^.Kind)) );
     Memo1.Lines.Add('');
   end;

   vValue := GetPropValue(Rectangle1, 'Name');               //test string
   Memo1.Lines.Add('Proprty Name = '+VarToStr(vValue) );

   vValue := GetPropValue(Rectangle1, 'Height');             //test float
   Memo1.Lines.Add('Property Height = '+VarToStr(vValue) );

   vValue := GetPropValue(Rectangle1, 'Sides');             //test enumeration
   Memo1.Lines.Add('Property Sides = '+VarToStr(vValue) );

   //The following would cause an exception
   {
   vValue := GetPropValue(Rectangle1, 'StrokeThickness');
   Memo1.Lines.Add('Property StrokeThickness ='+VarToStr(vValue));
   }

   Rectangle1.StrokeThickness := 5;   //works ??

   //Still fails after it was explicitly set
   {
   vValue := GetPropValue(Rectangle1, 'StrokeThickness');
   Memo1.Lines.Add('Property StrokeThickness ='+VarToStr(vValue));
   }

   Rectangle1.Stroke.Thickness := 10; //and also works... as expected

   //The following with cause an exception
   {
   vValue := GetPropValue(Rectangle1, 'StrokeDash');
   Memo1.Lines.Add('StrokeDash = '+VarToStr(vValue) );
   }

end;

end.

使用类似

的代码
var
  LProperty: TRttiProperty;
  LType: TRttiType;
  LContext: TRttiContext;
  LArray: TArray<TRttiProperty>;
begin
  LContext := TRTTIContext.Create;
  LType := LContext.GetType(TRectangle);
  LArray := LType.GetDeclaredProperties;
  for LProperty in LArray do
  begin
    Memo1.Lines.Add('Name: ' + LProperty.Name);
    Memo1.Lines.Add('PropType: ' + LProperty.PropertyType.Name);
    Memo1.Lines.Add('PropKind: ' + GetEnumName(TypeInfo(TTypeKind), Ord(LProperty.PropertyType.TypeKind)));
    if LProperty.IsReadable then
    begin
      Memo1.Lines.Add('Value: ' + LProperty.GetValue(Rectangle1).ToString);
    end
    else
    begin
      Memo1.Lines.Add('Value of the property cannot be read');
    end;
    Memo1.Lines.Add('');
  end;
end;