Pues sí, un evento no es más que un atributo de tipo “puntero a método”, que se accede a través de una propiedad de lectura y escritura en la sección published.
El primer paso será definir un nuevo tipo de dato, que sea un puntero a un método, del siguiente modo:
type
TEventoConversion = procedure(Sender: TObject; nuevoResultado: float) of object;
Con esto, hemos definido un nuevo tipo de dato que representa a un puntero a método. Posteriormente, podremos utilizarlo para declarar el evento de nuestro componente. El método “apuntado” utiliza dos argumentos: un objeto descendiente de TObject y un valor de tipo float.
Lo siguiente que tenemos que hacer es definir un atributo de este tipo, es decir: definir un atributo de tipo puntero a método. En el siguiente listado podéis ver cómo quedaría la sección private de nuestro componente después de declarar nuestro nuevo atributo de tipo puntero, que nos servirá para almacenar la dirección de un método. Este método que apuntaremos, será el que codifique el programador cuando haga doble clic sobre el evento.
TConversorMonedas = class(TComponent)
private
// declaración del puntero que contendrá la función del evento
FOnConversion: TEventoConversion;
FValorConvertir: float;
FValorConvertido: float;
FMonedaConvertir: TTipoMoneda;
FMonedaConvertido: TTipoMoneda;
...
El siguiente paso es la definición de una propiedad, en la sección published, que nos permita crear o modificar el evento en tiempo de diseño. Podéis ver la nueva sección published en el siguiente código:
published
property ValorConvertir: float
read FValorConvertir
write SetValorConvertir;
property ValorConvertido: float
read FValorConvertido;
property MonedaConvertir: TTipoMoneda
read FMonedaConvertir
write SetMonedaConvertir;
property MonedaConvertido: TTipoMoneda
read FMonedaConvertido
write FMonedaConvertido
// declaración del evento
property OnConversion: TEventoConversion
read FOnConversion
write FOnConversion
Una vez que hemos declarado el evento, podemos registrar el componente para ver si aparece en el Inspector de Objetos, como cualquier otro evento, tal y como podéis ver en la imagen de la derecha.
Bien, ya tenemos nuestro evento, y si hacemos doble clic sobre él, veremos que el entorno de Delphi nos crea un nuevo método, en la clase que representa al formulario con el siguiente aspecto:
procedure TForm1.ConversorMonedas1Convertido(Sender: TObject;
nuevoResultado: float);
begin
end;
En realidad, al hacer doble clic, lo que ha ocurrido es que Delphi nos ha definido un nuevo método en la clase del formulario, y ha asignado la dirección de este método a la propiedad OnConvertido del componente ¡Y todo esto de un simple ratonazo!
Esta es una de las grandes maravillas de Delphi, y otros lenguajes han utilizado la misma idea (como C# de Microsoft).
Pero nuestro componente no está completo, ya que el nuevo evento OnConvertido no se ejecutará nunca, porque nadie ha dicho que se ejecute ¿no?
Simplemente debemos hacer una llamada al evento, cada vez que se produzca una conversión. Esto, como sabemos, se produce dentro del procedimiento “Convertir”, así que, justo antes de asignar el resultado final, debemos llamar al evento. Hay que tener cuidado de llamar al evento sólo cuando el puntero tenga un valor distinto de nil ya que si tiene este valor, significará que el programador no ha hecho doble clic para codificar el evento, por lo que no debemos realizar la llamada, o se producirá un error de memoria. Esto lo podemos hacer con una simple comparación (if FOnConvertido <> nil then...) o bien utilizando la función Assigned, que sirve precisamente para esto: para ver si un puntero está apuntado (asignado) a algún sitio: if Assigned(FOnConvertido) then... Podéis ver el código final del método “Convertir” en el siguiente listado:
procedure TConversorMonedas.Convertir();
const
FactorAEuro: array[TTipoMoneda] of float =
(
1,
1.10,
2,
0.5,
0.18,
166.386
);
var
aux: float;
begin
// se calcula utilizando el euro como referencia.
aux := FValorConvertir / FactorAEuro[FMonedaConvertir];
// ahora aux contiene el valor origen en euros. Se pasa
// a la moneda destino
aux := aux * FactorAEuro[FMonedaConvertido];
// llamar al evento sólo si ha sido codificado
if Assigned(FOnConvertido) then
FOnConvertido(Self, aux);
FValorConvertido := aux;
end;
Bien, pues ya sabemos cómo definir eventos en nuestros propios componentes. Ahora nuestro conversor de monedas ha quedado más completo, y sabemos que el programador que lo utilice podrá realizar sus acciones en el nuevo evento.