![]() |
![]() |
| Volver al índice | Por Luis Roche
|
En está unidad aprenderemos a crear nuestros propios editores de propiedades. Los editores de propiedades son una poderosa herramienta que pueden marcar la diferencia entre un componente simplemente aceptable y uno realmente formidable. A través de ellos podremos dotar a nuestros componentes de nuevas características que van más allá de la siempre validación del valor de las propiedades. Gracias a ellos haremos la vida más fácil a los sufridos programadores que utilicen nuestros componentes, y eso siempre es de agradecer, ¿no? ;)
Hasta ahora nos hemos centrado en la creación de componentes sin preocuparnos de cómo se introducían los valores de las distintas propiedades definidas en ellos. Ha llegado el momento de centrarnos en este aspecto. Como todos sabemos para introducir y leer el valor de las propiedades en tiempo de diseño utilizamos el inspector de objetos. El inspector de objetos nos muestra en una primera columna el nombre de la propiedad y en una segunda el valor de dicha propiedad. Para introducir un nuevo valor lo tecleamos y listo. Pero esto es tan sólo apariencia. Los componentes interactuan con el inspector de objetos a través de los editores de propiedades. Desde el punto de vista del usuario, es el inspector de objetos el responsable de editar las propiedades, pero detrás de la escena hay una serie de objetos, los editores de propiedades, que se encargan de definir las capacidades de edición de las diversas propiedades de un componente.
En tiempo de diseño, cuando se selcciona un componente, el inspector de objetos crea instancias de los editores de propiedades necesarios para editar las propiedades definidas en el componente seleccionado. Cuando termina la edición, el mismo inpector de objetos destruye los editores de propiedades creados.
Delphi incluye una serie de editores de propiedades por defecto que son suficiente para la mayoría de las propiedades con las que trabajamos habitualmente. Pero como siempre, Delphi es tan potente que permite que creemos nuestros propios editores y que incluso reemplacemos los que una determinada propiedad tiene por defecto. ¡Chupate esa VB! ;)
Un editor de propiedades debe dar respuesta a dos aspectos principales:
Los pasos que es necesario seguir para escribir un editor de propiedades son los siguientes:
| Editor de propiedades | Tipo |
| TPropertyEditor | Clase base para todos los editores de propiedades |
| TIntegerProperty | Byte, word, integer, Longint |
| TCharProperty | Char |
| TEnumProperty | Tipos enumerados |
| TSetProperty | Sets |
| TFloatProperty | Single, Double, Extended, Comp, Currency |
| TStringProperty | Strings |
| TClassProperty | Cualquier objeto |
| TMethodProperty | Cualquier método (eventos) |
| TComponentProperty | Para propiedades que hacen referencia a componentes |
Más adelante profundizaremos en todos aspectos según vayamos desarrollando distintos editores, pero ahora ha llegado del momento de crear nuestro primer editor de propiedades.
Vamos a desarrollar nuestro primer editor de propiedades, que será editable sobre el propio inspector de objetos. Para ello pongámonos en situación: imaginemos que hemos creado un componente con una propiedad de tipo integer que guarda un valor que debe introducirse y visualizarse en base binaria y no en base decimal. Si no crearamos un editor de propiedades y dejaramos que Delphi utilizase el editor por defecto (TIntegerProperty en este caso) nos encontraríamos que tanto la introducción como la visualización del valor de la propiedad se realizaría en base decimal, que no es lo que queremos. Este es el típico caso en el que desarrollando un simple editor de propiedades ahorramos trabajo al futuro usuario de nuestro componente, evitando que tenga que hacer la conversión entre decimal y binario.
Como ya hemos comentado, tenemos que decidir de que clase vamos a derivar nuestro editor. En nuestro caso es fácil, ya que la propiedad almacenará un valor de tipo integer, así que ¿lo adivinais? ¡Si!, de TIntegerProperty ;)
Muy bien, nuestra propiedad almacena un entero, pero no queremos que el inspector de objetos nos muestre su valor decimal directamente, queremos efectuar una conversión de dicho valor decimal a base binaria y que sólo entonces el inspector de objetos nos muestre el valor. Para lograr esto debemos implementar (override) la función GetValue. Esta función, definida en TPropertyEditor, es llamada por el inspector de objetos para obtener la representación del valor de la propiedad en forma de string. Dentro de esta función debemos convertir el valor de la propiedad de decimal a binario y, a continuación, transformar este valor en string, ya que como ya hemos mencionado el inspector de objetos siempre muestra strings. De este modo la función GetValue queda así:
unit BinaryPropEd;
interface
uses DsgnIntf;
type
TBinIntegerProperty = class(TIntegerProperty)
public
function GetValue : string; override;
procedure SetValue(const Value : String); override;
end;
procedure Register;
implementation
Const
Bits16 : Array [1..16] of Integer = (32768,16384,8192,4096,2048,1024,512,256,128,64,32,16,8,4,2,1);
function TBinIntegerProperty.GetValue : string;
Var
Num, i : integer;
begin
Num:=GetOrdValue;
Result := '0000000000000000';
for i := 1 to 16 Do
if ( Num >= Bits16[i] ) Then
begin
Num := Num - Bits16[i];
Result[i] := '1';
end;
if ( Num > 0 ) Then
raise EPropertyError.Create('Error converting '+IntToStr(GetOrdValue) + ' to binary');
Insert('B',Result,1);
end;
...
end.
De la implementación de esta función la parte más importante es la primera línea:
Num:=GetOrdValueNecesitamos obtener el valor que tiene en ese momento la propiedad para trabajar sobre él. Para ello utilizamos el método GetOrdValue, definido de nuevo en TPropertyEditor, el cuál se encarga de devolver el valor de la propiedad en forma de ordinal (integer). De forma análoga, existen los métodos GetFloatValue, GetMethodValue, GetVarValue, etc. para utilizar con el tipo de propiedad correspondiente.
Una vez almacenado el valor de la propiedad en la variable Num, comienza la conversión del valor de decimal a binario, la cúal es fácil de entender. Cabe hacer notar que al máximo número de digitos binarios soportados es 16, margen más que suficiente para la mayoría de aplicaciones.
Por último tenemos que devolver un valor de tipo string como resultado de la función. Para ello vamos almacenando en la variable Result el string a devolver. Para finalizar, anteponemos la letra 'B' a la cadena para indicar que se trata de base binaria.
Y eso es todo en lo que a esta función respecta. De este modo, cuando el inspector de objetos deba mostrar el valor de la propiedad, llamará a el método GetValue el cuál le devolverá el string correspondiente. Pero nos queda la otra mitad: estaría bien que pudieramas introducir el valor de la propiedad tanto en decimal como en binario según nos interesase, ¿verdad? pues vamos a ello. ;)
Para conseguir esta funcionalidad debemos implementar (override) el método SetValue, definido de nuevo en la clase TPropertyEditor. Cuando el usuario entra un nuevo valor usando el inspector de objetos, este llama al método SetValue, el cuál debe efectuar la traducción inversa a la efectuada por el método GetValue. Es decir, debe convertir el string que contiene el nuevo valor de la propiedad al tipo de datos de dicha propiedad. En nuestro caso, el string vendrá en base decimal o binaria (en este último caso, la primera letra de la cadena será una 'B') y convertirlo a base decimal. Para ello implementaremos el método SetValue de la siguiente forma:
...
type
TBinIntegerProperty = class(TIntegerProperty)
public
function GetValue : string; override;
procedure SetValue(const Value : String); override;
end;
procedure Register;
implementation
...
procedure TBinIntegerProperty.SetValue(const Value : String);
Var
i, Total, Longitud : integer;
NumText : string;
begin
if UpperCase(Value[1])='B' then
begin
NumText:=Copy(Trim(Value),2,Length(Trim(Value))-1);
NumText:=Copy('0000000000000000',1,16-Length(NumText)) + NumText;
Total:=0;
for i:=1 to Length(NumText) do
begin
if not (NumText[i] in ['0','1']) then
raise EPropertyError.Create(NumText[i] + ' is not a valid binary digit')
else if NumText[i]='1' then
Total:=Total+Bits16[i];
end;
SetOrdValue(Total);
end
else
SetOrdValue(StrToInt(Value));
end;
...
end.
En la implementación de este método primero comprobamos si el usuario ha introducido el nuevo valor de la propiedad en base decimal o en base binaria. En el primer caso, tan sólo hay que convertir el string a integer mediante la función StrToInt(Value) y, a continuación, utilizar el método SetOrdValue para almacenar el valor correspondiente. De forma análoga, según el tipo de la propiedad, existen los métodos SeFloatValue, etc.
En el caso de que la primera letra de la cadena sea una 'B', se convierte la cadena con el valor binario a base decimal y se vuelve a utilizar el método SetOrdValue para almacenar el valor en la propiedad.
Una vez implementados estos dos métodos (GetValue y SetValue) ya tenemos nuestro editor de propiedades terminado; tan sólo nos queda un pequeño, pero indispensable paso, registrarlo en la VCL.
De igual manera que debemos registrar en la VCL los componentes, con los editores de propiedades debemos hacer lo mismo. Par ello disponemos del método RegisterPropertyEditor (definido en la unidad DsgnIntf) que tiene la siguiente declaración:
procedure RegisterPropertyEditor(PropertyType : PTypeInfo; ComponentClass : TClass;
const PropertyName : string; EditorClass : TPropertyEditorClass);
PropertyType hace referencia al tipo de la propiedad al que se aplicará el editor de propiedades. Para suministrar un valor a este parámetro normalmente utilizaremos la función TypeInfo, por ejemplo, TypeInfo(integer).
ComponentClass permite restringir el uso del editor de propiedades a la clase específica. Un valor de nil registra el editor para todos los componentes.
PropertyName especifica el nombre de la propiedad. Un valor distinto de nil registra el editor sólo para la propiedad especificada, mientras que un valor '' lo registra para todas las propiedades
EditorClass especifica el editor de propiedades que se registra (la clase).
Jugando con estos parámetros, tenemos a nuestra disposición un amplio abánico de posibilidades para registrar nuestro editor de propiedades. Vemos algunos ejemplos con nuestro recién creado editor:
Os aconsejo que registreis el editor de la forma más global posible para que experimenteis con él. Luego, una vez os canseis de ver todas las propiedades enteras en binario, os permito que le desinstaleis ;)
También podéis crearos un componente de "mentirijillas" y registrar sólo el editor para el mismo. Algo así:
...
Type
TMiComponente = class(TComponent)
...
property PropiedadBinaria : integer read FPropBin write FPropBin;
...
end;
...
Como ya mencionamos al mostrar los pasos que se deben seguir para crear un editor de propiedades, el primero de ellos es crear una unidad donde situar el editor. En ese momento dijimos que no era una elección tan trivial como pudiera parecer en un pricincipio. ¿En que unidad debemos situar el editor? ¿en la misma unidad donde está el componente que tiene la propiedad que será editada?, ¿en una unidad aparte? Tenemos tres posibles ubicaciones:
La primera opción es situar el editor de propiedades en la misma unidad en que reside el componente que tiene la propiedad que utiliza el editor de propiedades. Esta es la opción más intuitiva; sin embargo no es la más recomendable, sobre todo si el editor utiliza un cuadro de diálogo.
El motivo es que Delphi sólo utiliza el editor de propiedades en tiempo de diseño. De hecho, el form que contiene el cuadro de diálogo no es enlazado con la aplicación, ni tampoco la clase del editor de propiedades. Sin embargo, si se enlazan los recursos asociados al cuadro de diálogo, recursos que en tiempo de ejecución lo único que hacen es ocupar espacio, ya que no se utilizarán para nada. Por tanto, estamos incrementando el tamño del ejecutable a lo tonto :( Por otra parte, si el editor de propiedades no utiliza cuadros de diálogo, el efecto no es tan pernicioso pero sigue sin ser recomendable.
La segunda opción es situar el editor de propiedades (si utiliza un form cómo cuadro de diálogo) en la misma unidad del cuadro de diálogo. De este modo, la aplicación que usa el componente no enlaza el editor de propiedades ni sus recursos asociados.
La tercera opción es situar el editor de propiedades en una unidad de registro. Una unidad de registro es una unidad normal y corriente que agrupa varias sentencias de registro correspondientes a distintos componentes y editores de propiedades que residen en distintas unidades. Esta es la opción más recomendable si el editor de propiedades es utilizado por varios componentes.
Por tanto, las dos últimas opciones son las más recomendables dependiendo la elección de una u otra del uso que se vaya a dar al editor de propiedades. De todas formas, no olvideis añadir a la clausula uses las unidades correspondientes según donde situeis el editor.
En próximas unidades veremos ejemplos de distintas ubicaciones de los editores de propiedades que iremos construyendo.
unit BinaryPropEd;
interface
uses DsgnIntf;
type
TBinIntegerProperty = class(TIntegerProperty)
public
function GetValue : string; override;
procedure SetValue(const Value : String); override;
end;
procedure Register;
implementation
Const
Bits16 : Array [1..16] of Integer = (32768,16384,8192,4096,2048,1024,512,256,128,64,32,16,8,4,2,1);
function TBinIntegerProperty.GetValue : string;
Var
Num, i : integer;
begin
Num:=GetOrdValue;
Result := '0000000000000000';
for i := 1 to 16 Do
if ( Num >= Bits16[i] ) Then
begin
Num := Num - Bits16[i];
Result[i] := '1';
end;
if ( Num > 0 ) Then
raise EPropertyError.Create('Error converting '+IntToStr(GetOrdValue) + ' to binary');
Insert('B',Result,1);
end;
procedure TBinIntegerProperty.SetValue(const Value : String);
Var
i, Total, Longitud : integer;
NumText : string;
begin
if UpperCase(Value[1])='B' then
begin
NumText:=Copy(Trim(Value),2,Length(Trim(Value))-1);
NumText:=Copy('0000000000000000',1,16-Length(NumText)) + NumText;
Total:=0;
for i:=1 to Length(NumText) do
begin
if not (NumText[i] in ['0','1']) then
raise EPropertyError.Create(NumText[i] + ' is not a valid binary digit')
else if NumText[i]='1' then
Total:=Total+Bits16[i];
end;
SetOrdValue(Total);
end
else
SetOrdValue(StrToInt(Value));
end;
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(Integer),nil,'',TBinIntegerProperty);
end;
end.
Otros Links de Interés:
Cocina - Videos - Juegos Gratis - Postales cachondas - Cine - Programas Gratis - Letras de Canciones
Listas de todos los Tutoriales Gratis. 1998- 2007 - -
Los
tutoriales y cursos aquí reunidos son una recopilación de los mejores encontrados en
Internet.
El crédito y copyright de los mismos si lo hubiere corresponde al autor de cada
uno de ellos.
Si tu tutorial o curso está aquí, y deseas darlo de baja de esta recopilación o
quieres añadir el tuyo,
envíanos un mensaje desde
aquí