При использовании RTTI как мы можем получить и установить более глубокие свойства уровня?
обзор
Я ценю пару подобных вопросов, которые уже были заданы раньше:
- Get / Set sub properties ussing RTTI
- получить sub свойство компонента в Delphi с помощью RTTI
- как я могу установить значение вложенного свойства с помощью RTTI
- как я могу установить / получить значение свойства через RTTI для компексных вещей, таких как TStringGrid.Клетки?
однако я не продвинулся дальше в понимании того, как именно 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:
как и процедуры, показанные ранее, мне нужно, чтобы то же самое произошло для тех свойств 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
вот пример кода запрос:
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;