A veces durante la ejecución de código necesitamos modificar una determinada propiedad de diferentes tipos de controles. Para la explicación podemos pensar por ejemplo en como desactivar (Enabled=False) todos los controles de un formulario; Una primera opción podría ser un código como éste:
// recorrer los controles
for i := 0 to (Self.ComponentCount - 1) do begin
// Es un Edit
if (Components[i] is TEdit) then
TEdit(Components[i]).Enabled := False;
// Es un LAbel
if (Components[i] is TLabel) then
TLabel(Components[i]).Enabled := False;
// Es un ListBox
if (Components[i] is TListBox) then
TListBox(Components[i]).Enabled := False;
...
end; |
// recorrer los controles
for i := 0 to (Self.ComponentCount - 1) do begin
// Es un Edit
if (Components[i] is TEdit) then
TEdit(Components[i]).Enabled := False;
// Es un LAbel
if (Components[i] is TLabel) then
TLabel(Components[i]).Enabled := False;
// Es un ListBox
if (Components[i] is TListBox) then
TListBox(Components[i]).Enabled := False;
...
end;
Una implementación como ésta tiene muchos problemas e inconvenientes; Los más claros podrían ser:* Es poco «ortodoxa», por decirlo así;
- Es un código repetitivo y nada eficiente.
- Es por definición incompleta, ya que el número de componentes que podemos tener en un form es inmenso y de muchas clases (yo sólo las básicas que trae Delphi).
- Es poco flexible.
- …
Utilizando RTTI podemos de forma relativamente sencilla modificar todos los componentes utilizando una única instrucción.
Podemos utilizar un procedimiento como éste:
{:Asigna valor a una propiedad a través del Nombre (RTTI). }
function SetPropAsString(AObj: TObject; const PropName, Value: String):Boolean;
var
PInfo: PPropInfo;
begin
// Intentamos acceder (con un puntero) a la info. de la propiedad
PInfo := GetPropInfo(AObj.ClassInfo, PropName);
Result := PInfo <> nil;
// Se ha obtenido la información...
if (Result) then begin
// Se ha encontrado la propiedad con éste nombre; Chequear el tipo...
if (PInfo^.Proptype^.Kind = tkString) or
(PInfo^.Proptype^.Kind = tkLString) then begin
// Asignar el valor de tipo String
SetStrProp(AObj, PInfo, Value);
end
else if (PInfo^.Proptype^.Kind = tkInteger) then begin
// Asignar el valor...
if (PInfo^.PropType^.Name = 'TColor') then begin
SetOrdProp(AObj, PInfo, StringToColor(Value));
end
else if (PInfo^.PropType^.Name = 'TCursor') then begin
SetOrdProp(AObj, PInfo, StringToCursor(Value));
end
else begin
SetOrdProp(AObj, PInfo, StrToInt(Value));
end;
end
else if (PInfo^.Proptype^.Kind = tkEnumeration) then begin
// Bloque de proteccion
try
if (PInfo^.PropType^ = TypeInfo(System.Boolean)) then begin
SetOrdProp(AObj, PInfo, StrToInt(Value));
end
else begin
SetOrdProp(AObj, PInfo, StrToInt(Value));
end;
except
raise;
end;
end
else begin
Result := False;
end;
end
else begin
// No se ha encontrado la propiedad con ese nombre
Result := False;
end;
end; |
{:Asigna valor a una propiedad a través del Nombre (RTTI). }
function SetPropAsString(AObj: TObject; const PropName, Value: String):Boolean;
var
PInfo: PPropInfo;
begin
// Intentamos acceder (con un puntero) a la info. de la propiedad
PInfo := GetPropInfo(AObj.ClassInfo, PropName);
Result := PInfo <> nil;
// Se ha obtenido la información...
if (Result) then begin
// Se ha encontrado la propiedad con éste nombre; Chequear el tipo...
if (PInfo^.Proptype^.Kind = tkString) or
(PInfo^.Proptype^.Kind = tkLString) then begin
// Asignar el valor de tipo String
SetStrProp(AObj, PInfo, Value);
end
else if (PInfo^.Proptype^.Kind = tkInteger) then begin
// Asignar el valor...
if (PInfo^.PropType^.Name = 'TColor') then begin
SetOrdProp(AObj, PInfo, StringToColor(Value));
end
else if (PInfo^.PropType^.Name = 'TCursor') then begin
SetOrdProp(AObj, PInfo, StringToCursor(Value));
end
else begin
SetOrdProp(AObj, PInfo, StrToInt(Value));
end;
end
else if (PInfo^.Proptype^.Kind = tkEnumeration) then begin
// Bloque de proteccion
try
if (PInfo^.PropType^ = TypeInfo(System.Boolean)) then begin
SetOrdProp(AObj, PInfo, StrToInt(Value));
end
else begin
SetOrdProp(AObj, PInfo, StrToInt(Value));
end;
except
raise;
end;
end
else begin
Result := False;
end;
end
else begin
// No se ha encontrado la propiedad con ese nombre
Result := False;
end;
end;
Su utilización es muy sencilla, y en el ejemplo anterior el código utilizado pasaría a ser similar a éste:
NOTA: Añadir la unit TypInfo al USES.
NOTA2: (Gracias Arsenio por comentarmelo) Se deben añadir al uses también las units Graphics y Controls ya que son usados por StringToColor y StringToCursor respectivamente.
// recorrer los controles
for i := 0 to (Self.ComponentCount - 1) do begin
SetPropAsString(Components[i], 'Enabled', '0'{FALSE});
end;
// Otros ejemplos
SetPropAsString(Components[i], 'Left', '10');
SetPropAsString(Components[i], 'Color', 'clRed');
... |
// recorrer los controles
for i := 0 to (Self.ComponentCount - 1) do begin
SetPropAsString(Components[i], 'Enabled', '0'{FALSE});
end;
// Otros ejemplos
SetPropAsString(Components[i], 'Left', '10');
SetPropAsString(Components[i], 'Color', 'clRed');
...
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,…