Modificar propiedades de controles en ejecución utilizando RTTI
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; |
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; |
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'); ... |



