Éste truco aparece debido a la necesidad de obtener vía RTTI la siguiente información: Lista de todas las propiedades de un componente.
Valor de una propiedad de un componente sin utilizar el método siguiente (*).
Una primera aproximación para obtener la propiedad (Left, por ejemplo) de un componente del que a priori no conocemos el tipo, es utilizar una estructura IF o CASE (método 1) similar a la siguiente:
1
2
3
4
5
6
7
8
| // Metodo 1 (*)
// Segun el tipo de componente...
if (comp is TEdit) then
Result := TEdit(comp).Left;
else if (comp is TLabel) then
Result := TLabel(comp).Left;
else if (comp is TButton) then
Result := TButton(comp).Left; |
// Metodo 1 (*)
// Segun el tipo de componente...
if (comp is TEdit) then
Result := TEdit(comp).Left;
else if (comp is TLabel) then
Result := TLabel(comp).Left;
else if (comp is TButton) then
Result := TButton(comp).Left;
Está claro que éste método es poco eficiente, poco flexible (siempre podemos dejarnos algún tipo de componente) y poco ortodoxo.
¿Cómo debe hacerlo Delphi para mostrar las propiedades de un componente en el inspector de objetos (por ejemplo)?
La respuesta es RTTI. Se puede acceder a las propiedades de un componente (a partir de la clase) y del nombre de la propiedad, utilizando la siguiente función (he añadido un parámetro nuevo para obtener en la misma llamada la lista de todas las propiedades -TStrings-).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| {:Obtener la información de una propiedad a partir de la clase y el nombre;
Ademas devuelve la lista de todas las porpiedades de ese control.}
function GetRTTIControlInfo(AControl: TPersistent;
propList:TStrings; AProperty: string): PPropInfo;
var
i: integer;
props: PPropList;
tData: PTypeData;
begin
// Inicial
Result := nil;
// No asignado el control ==> Salimos
if (AControl = nil) or (AControl.ClassInfo = nil) then begin
Exit;
end;
// Obtener la información
tData := GetTypeData(AControl.ClassInfo);
// Tipo desconocido o sin propiedades ==> Salimos
if (tData = nil) or (tData^.PropCount = 0) then
Exit;
GetMem(props, tData^.PropCount * SizeOf(Pointer));
try
GetPropInfos(AControl.ClassInfo, props);
for i := 0 to tData^.PropCount - 1 do begin
propList.Add(Props^[i]^.Name);
with Props^[i]^ do begin
if (Name = AProperty) then begin
result := Props^[i];
end;
end;
end;
finally
FreeMem(props);
end;
end; |
{:Obtener la información de una propiedad a partir de la clase y el nombre;
Ademas devuelve la lista de todas las porpiedades de ese control.}
function GetRTTIControlInfo(AControl: TPersistent;
propList:TStrings; AProperty: string): PPropInfo;
var
i: integer;
props: PPropList;
tData: PTypeData;
begin
// Inicial
Result := nil;
// No asignado el control ==> Salimos
if (AControl = nil) or (AControl.ClassInfo = nil) then begin
Exit;
end;
// Obtener la información
tData := GetTypeData(AControl.ClassInfo);
// Tipo desconocido o sin propiedades ==> Salimos
if (tData = nil) or (tData^.PropCount = 0) then
Exit;
GetMem(props, tData^.PropCount * SizeOf(Pointer));
try
GetPropInfos(AControl.ClassInfo, props);
for i := 0 to tData^.PropCount - 1 do begin
propList.Add(Props^[i]^.Name);
with Props^[i]^ do begin
if (Name = AProperty) then begin
result := Props^[i];
end;
end;
end;
finally
FreeMem(props);
end;
end;
Un ejemplo de utilización podría ser el siguiente:
(basta con un formulario que tenga un TButton y un TMemo)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| procedure TForm1.Button1Click(Sender: TObject);
var
pInfo: PPropInfo;
propList:TStrings;
PStr:String;
begin
// Crear la lista
propList := TStringList.Create();
// protección para liberar
try
// Acceder a la info de la propiedad
pInfo := GetRTTIControlInfo(Button1, propList, 'Left');
// La prop. encontrada es de tipo integer (debería ser, ya que es la
// prop. Left)
if (pInfo.PropType^^.Kind in [tkInteger]) then begin
// Obtener
PStr:= 'Left' + ' = ' + Format('%d', [GetOrdProp(Button1, pInfo)]);
// Mostrar
MessageDlg(PStr, mtInformation, [mbOK], 0);
end;
// Rellenar la lista de propiedades
Memo1.Lines.Assign(propList);
finally
FreeAndNil(propList);
end;
end; |
procedure TForm1.Button1Click(Sender: TObject);
var
pInfo: PPropInfo;
propList:TStrings;
PStr:String;
begin
// Crear la lista
propList := TStringList.Create();
// protección para liberar
try
// Acceder a la info de la propiedad
pInfo := GetRTTIControlInfo(Button1, propList, 'Left');
// La prop. encontrada es de tipo integer (debería ser, ya que es la
// prop. Left)
if (pInfo.PropType^^.Kind in [tkInteger]) then begin
// Obtener
PStr:= 'Left' + ' = ' + Format('%d', [GetOrdProp(Button1, pInfo)]);
// Mostrar
MessageDlg(PStr, mtInformation, [mbOK], 0);
end;
// Rellenar la lista de propiedades
Memo1.Lines.Assign(propList);
finally
FreeAndNil(propList);
end;
end;