При использовании RTTI как мы можем получить и установить более глубокие свойства уровня?

обзор

Я ценю пару подобных вопросов, которые уже были заданы раньше:

однако я не продвинулся дальше в понимании того, как именно RTTI можно использовать для моих нужд.

Я также вложил много времени и усилий в написание этого вопроса, поэтому я надеюсь, что он не будет закрыт :)

Рабочие Примеры

у меня есть несколько процедур, ниже которых можно вывести в список TStrings имена свойств, значения и типы компонента. Первоисточник не мой, я только что сделала незначительные изменения в нем, убрали код и поместили их в некоторые аккуратные многоразовые процедуры:


ниже будут выведены имена свойств, такие как:

  • цвета
  • свойство управляет
  • включено
  • Высота
  • ширина
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, tkAny, 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
  • правда
  • 25
  • 75
function GetComponentPropertyValue(Component: TComponent; APropName: string): string;
var
  I: Integer;
  Count, Size: Integer;
  PropList: PPropList;
  PropInfo: PPropInfo;
begin
  Count := GetPropList(Component.ClassInfo, tkAny, nil);
  Size  := Count * SizeOf(Pointer);
  GetMem(PropList, Size);
  try
    Count := GetPropList(Component.ClassInfo, tkAny, 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
  • целое
  • целое
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;

бок о бок вывод для каждой процедуры, вызванной выше, покажет что-то вроде этого:

  • цвет / clWindow / TColor
  • DoubleBuffered / False / Boolean
  • Включено / True / Boolean
  • Высота | 25 | Целое Число
  • Width | 75 / Integer

вопрос

все вышеперечисленное на данный момент работает, нет никаких проблем, кроме того, что мне нужно некоторое время читать документация немного больше, чтобы попытаться получить немного лучшее понимание и быть в состоянии переварить все это.

мой вопрос (который беспокоил меня в течение нескольких дней) заключается в том, как правильно получить и установить свойства sub. Например, взгляните на этот скриншот (специально измененный) инспектора объектов Delphi:

enter image description here

как и процедуры, показанные ранее, мне нужно, чтобы то же самое произошло для тех свойств sub, которые я выделил в синий.

в идеале, что я хотел бы, это функция, где я могу передать компонент и имя свойства и вернуть True, если он имеет sub свойства, так что что-то вроде:

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

Я не уверен, насколько хорошо это будет работать, хотя, как видно из скриншота, некоторые свойства sub также имеют свойства sub (например, компонент>шрифт>стиль).

в конечном счете, то, что я хотел бы, это способ извлечения имен, значений и типов свойств sub. Так что например:

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

для свойств вложенного шрифта, например:

  • кодировка / DEFAULT_CHARSET / TFontCharset
  • цвет / clWindowText / TColor
  • Высота | -11 | Целое Число
  • Ориентация | 0 | Целое Число
  • шаг / fpDefault / TFontPitch
  • качество | fqDefault / TFontQuality
  • Size | 8 / Integer

доступ к другому вложенному свойству (шрифт.Style), то ставит еще одну проблему, если не используется такая процедура:

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

это становится довольно глупо.


резюме

в основном мне нужен способ копаться в более глубоких свойствах уровня, чтобы получить имена, значения и типы из них, поместить их в список, а также иметь возможность изменять значения.

Я был бы признателен, если бы кто-то мог написать код примеры того, как я могу этого достичь (бонус с некоторыми комментариями в коде). Я уверен,что для некоторых это будет относительно простая задача, но я нахожу ее очень тривиальной.

прочитав различные документы и примеры до сих пор оставляет меня довольно невежественным, чтобы быть честным, основная проблема не зная, какие типы использовать или Как правильно создавать и управлять ими.

1 ответов


Sub свойства, такие как TFont, TAction, TPopupMenu уже компоненты (классы), созданные в компоненте владельца, как TButton.

чтобы узнать тип свойства use PropInfo.PropType^.Kind

см. Delphi help

TypInfo.PTypeInfo Типа

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;

пример для получения sub класс!--10-->

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;

обновление

этот код поможет вам понять 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, tkAny, nil);
  Size  := Count * SizeOf(Pointer);
  GetMem(PropList, Size);
  try
    Count := GetPropList(Component.ClassInfo, tkAny, 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;