delphi – 当使用RTTI时,我们如何获取和设置更深层次的子属性?

概观

我很感激之前已经提出过几个类似的问题:

> Get/Set sub properties ussing RTTI
> Get a sub property of a component in Delphi using RTTI
> how i can set the value of a nested property using the RTTI
> How can I set/get property value through RTTI for compex things like TStringGrid.Cells?

但是,我没有进一步了解如何将RTTI用于我的需求.

我也花了很多时间和精力来写这个问题所以我希望它不会被关闭:)

工作实例

我在下面有几个过程可以输出到TStrings列表中的组件的属性名称,值和类型.原始来源不是我的,我只是做了一些小改动,整理了代码并将它们放入一些整洁的可重用程序:

下面将输出属性名称,例如:

  • Color
  • DoubleBuffered
  • Enabled
  • Height
  • Width
procedure GetComponentPropertyNames(Component: TComponent; OutList: TStrings);
var
  I: Integer;
  Count,Size: Integer;
  PropList: PPropList;
  PropInfo: PPropInfo;
begin
  OutList.BeginUpdate;
  try
    OutList.Clear;

    Count := GetPropList(Component.ClassInfo,tkAny,nil);
    Size  := Count * SizeOf(Pointer);
    GetMem(PropList,Size);
    try
      Count := GetPropList(Component.ClassInfo,PropList);
      for I := 0 to Count -1 do
      begin
        PropInfo := PropList^[I];
        if not (PropInfo^.PropType^.Kind = tkMethod) then
        begin
          OutList.Add(PropInfo^.Name);
        end;
      end;
    finally
      FreeMem(PropList);
    end;
  finally
    OutList.EndUpdate;
  end;
end;

以下将输出属性值,例如:

  • clWindow
  • False
  • True
  • 25
  • 75
function GetComponentPropertyValue(Component: TComponent; APropName: string): string;
var
  I: Integer;
  Count,Size: Integer;
  PropList: PPropList;
  PropInfo: PPropInfo;
begin
  Count := GetPropList(Component.ClassInfo,nil);
  Size  := Count * SizeOf(Pointer);
  GetMem(PropList,Size);
  try
    Count := GetPropList(Component.ClassInfo,PropList);
    for I := 0 to Count -1 do
    begin
      PropInfo := PropList^[I];
      if not (PropInfo^.PropType^.Kind = tkMethod) then
      begin
        if SameText(PropInfo^.Name,APropName) then
        begin
          Result := System.Variants.VarToStr(GetPropValue(Component,PropInfo^.Name));
          Exit;
        end;
      end;
    end;
  finally
    FreeMem(PropList);
  end;
end;

procedure GetComponentPropertyValues(Component: TComponent; OutList: TStrings);
var
  SL: TStringList;
  I: Integer;
begin
  SL := TStringList.Create;
  try
    GetComponentPropertyNames(Component,SL);
    for I := 0 to SL.Count -1 do
    begin
      OutList.Add(GetComponentPropertyValue(Component,SL.Strings[I]));
    end;
  finally
    SL.Free;
  end;
end;

最后,下面将以字符串格式输出属性类型,例如:

  • TColor
  • Boolean
  • Boolean
  • Integer
  • Integer
function GetComponentPropertyType(Component: TComponent; APropName: string): string;
var
  SL: TStringList;
  I: Integer;
  PropInfo: TPropInfo;
  PropTypeName: string;
begin
  SL := TStringList.Create;
  try
    GetComponentPropertyNames(Component,SL);
    for I := 0 to SL.Count -1 do
    begin
      PropInfo := GetPropInfo(Component,SL.Strings[I])^;
      if SameText(PropInfo.Name,APropName) then
      begin
        PropTypeName := PropInfo.PropType^.Name;
        Result := PropTypeName;
        Exit;
      end;
    end;
  finally
    SL.Free;
  end;
end;

procedure GetComponentPropertyTypes(Component: TComponent; OutList: TStrings);
var
  SL: TStringList;
  I: Integer;
begin
  SL := TStringList.Create;
  try
    GetComponentPropertyNames(Component,SL);
    for I := 0 to SL.Count -1 do
    begin
      OutList.Add(GetComponentPropertyType(Component,SL.Strings[I]));
    end;
  finally
    SL.Free;
  end;
end;

并排上面调用的每个过程的输出显示如下:

  • Color | clWindow | TColor
  • DoubleBuffered | False | Boolean
  • Enabled | True | Boolean
  • Height | 25 | Integer
  • Width | 75 | Integer

以上所有这些都是有效的,除了我需要花一些时间阅读文档以尝试获得更好的理解并能够消化它之外没有其他问题.

我的问题(现在一直困扰我几天)是如何正确获取和设置子属性.例如,看一下Delphi Object Inspector的截图(为了目的而修改):

就像之前显示的程序一样,我需要对以蓝色突出显示的子属性发生同样的事情.

理想情况下,我想要的是一个函数,我可以传递一个组件和一个属性名称,如果它有子属性,则返回True,如下所示:

function HasSubProperty(Component: TComponent; APropName: string): Boolean;
begin
  Result := ??
end;

虽然从屏幕截图中可以看出,我不确定这会有多好,但是一些子属性也有子属性(例如Component> Font> Style).

最终我想要的是一种检索子属性名称,值和类型的方法.所以类似于:

procedure GetComponentSubPropertyNames(Component: TComponent; APropName: string;
  OutList: TStrings);
begin
  //
end;

被叫时:

GetComponentSubPropertyNames(Label1,Anchors);

应检索:

  • akLeft
  • akTop
  • akRight
  • akBottom

使用类似的过程来获取值和类型将如下所示:

  • akLeft | True | Boolean
  • akTop | True | Boolean
  • akRight | True | Boolean
  • akBottom | True | Boolean

对于Font子属性,例如:

  • Charset | DEFAULT_CHARSET | TFontCharset
  • Color | clWindowText | TColor
  • Height | -11 | Integer
  • Orientation | 0 | Integer
  • Pitch | fpDefault | TFontPitch
  • Quality | fqDefault | TFontQuality
  • Size | 8 | Integer

访问另一个属性(Font.Style)然后引起另一个问题,除非使用这样的过程:

procedure GetComponentSubPropertySubPropertyNames(Component: TComponent; APropName,ASubPropName: string; OutList: TStrings);
begin
  //
end;

这变得相当愚蠢.

摘要

基本上我需要一种挖掘更深层次属性方法获取它们的名称,值和类型,将它们放在列表中,并且还能够更改值.

我很感激,如果有人可以写一些代码样本,我将如何实现这一点(代码中的一些评论奖励).我相信一些这将是一个相对容易的任务,但我发现它确实非常微不足道.

到目前为止阅读了各种文档和示例仍然让我相当坦白,主要关注的是不知道使用什么类型或如何正确地创建和管理它们.

解决方法

像TFont,TAction,TPopupMenu这样的子属性是已经在像TButton这样的所有者组件中创建的组件(类).

要知道属性类型,请使用PropInfo.PropType ^ .Kind

请参阅Delphi帮助

TypInfo.PTypeInfo Type

TypInfo.TTypeKind

以下是您请求的代码示例:

function HasSubProperty(Component: TComponent; APropName: string): Boolean;
var PropInfo: TPropInfo;
begin
  PropInfo := GetPropInfo(Component,APropName)^;
  Result := PropInfo.PropType^.Kind in [tkClass,tkSet,tkRecord]
end;

获取子类的示例

function GetSubPropClass(Component: TComponent; APropName: string):TComponent;
    var PropInfo: PPropInfo;
        AObject : TObject;
    begin
      Result := nil;
      PropInfo := GetPropInfo(Component,APropName);
      if PropInfo.PropType^.Kind in [tkClass] then
      begin
        AObject := GetobjectProp(Component,PropInfo);
        if Assigned(AObject) then
          Result := TComponent(AObject);
      end;
    end;

示例使用它

procedure TForm1.Button1Click(Sender: TObject);
var AComp : TComponent;
begin
  AComp := GetSubPropClass(Form1,'TFont',ListBox4.Items);
  if AComp <> nil then
    GetComponentPropertyNames(AComp);
end;

UPDATE

代码将帮助您了解SET属性

function GetComponentPropertyValue(Component: TComponent; APropName: string): string;
var
  I,X: Integer;
  Count,Size: Integer;
  PropList: PPropList;
  PropInfo: PPropInfo;
  PropTypeInf : PTypeInfo;
  SetList : TStrings;
  SetName,SetVal : string;
begin
  Count := GetPropList(Component.ClassInfo,PropList);
    for I := 0 to Count -1 do
    begin
     PropTypeInf := PropList^[I]^.PropType^;
     PropInfo := PropList^[I];
      if not (PropInfo^.PropType^.Kind = tkMethod) then
      begin
        if SameText(PropInfo^.Name,APropName) then
        begin

          if (PropInfo^.PropType^.Kind = tkSet) then
          begin
            try
              SetList := TStringList.Create;
              SetList.CommaText := System.Variants.VarToStr(GetPropValue(Component,PropInfo^.Name));
              for X := 0 to 255 do
              begin
                SetName := GetSetElementName(GetTypeData(PropTypeInf)^.CompType^,X);
                if Containsstr(SetName,'UITypes') then break;
                SetVal := SetName + ' = ' + IfThen(SetList.IndexOf(SetName)<>-1,'True','False');
                if Result = '' then
                  Result := SetVal else
                  Result := Result + ',' + SetVal;
              end;

            finally
              SetList.Free;
            end;
          end else
            Result := System.Variants.VarToStr(GetPropValue(Component,PropInfo^.Name));
          Exit;
        end;
      end;
    end;
  finally
    FreeMem(PropList);
  end;
end;

相关文章

 从网上看到《Delphi API HOOK完全说明》这篇文章,基本上都...
  从网上看到《Delphi API HOOK完全说明》这篇文章,基本上...
ffmpeg 是一套强大的开源的多媒体库 一般都是用 c/c+&#x...
32位CPU所含有的寄存器有:4个数据寄存器(EAX、EBX、ECX和ED...
1 mov dst, src dst是目的操作数,src是源操作数,指令实现的...