带有TRectangle的GetPropList返回StrokeThickness作为属性,该属性应该是Stroke类的一部分

时间:2016-02-20 18:22:03

标签: delphi rtti delphi-10-seattle

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

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

  1. StrokeThickness类型tkFloat
  2. StrokeCap类型tkEnumeration
  3. StrokeDash类型tkEnumeration
  4. StrokeJoin作为类型tkEnumeration。
  5. GetPropList确实正确返回Stroke作为tkClass类型,这是我所期望的,并且在解析时,Stroke类返回ThicknessCapDashJoin,我可以从这些中获取正确的值。

    问题是在GetPropValue上执行StrokeThickness会导致异常。因此,我必须特别注意GetPropList返回的“损坏”属性,我希望避免这种情况。

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

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

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

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

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

    表格:

    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.
    

1 个答案:

答案 0 :(得分:1)

使用像

这样的代码
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;
相关问题