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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 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:
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | {: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.
1 2 3 4 5 6 7 8 | // 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'); ... |



