É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;
Embarcadero MVP.
Analista y Programador de Sistemas Informáticos.
Estudios de Informática (Ingeniería Técnica Superior) en la UPC (Universidad Politécnica de Barcelona).
Llevo utilizando Delphi desde su versión 3. Especialista en diseño de componentes, Bases de Datos, Frameworks de Persistencia, Integración Continua, Desarrollo móvil,…