Jump to content


Photo

Concepto: Eventos


  • Please log in to reply
12 replies to this topic

#1 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 19 December 2008 - 02:03 PM

Hola nuevamente,

En esta oportunidad tocaremos el tema de los eventos. Es un poco más complicado de entender, y lamentablemente se necesita ser un poco más técnico.
Lo bueno de ver este tema es que se puede comprender como es que funcionan los eventos en Delphi, acercándonos más a los ricos conceptos POO.

Intuitivamente hacemos uso de eventos constantemente: OnClick, OnDblClick, OnChange, OnActivate, OnCloseQuery, OnClose, etc. ¿Pero que son en realidad? ¿Cómo y cuando se ejecutan?

De foma muy vaga, decimos que un evento es una rutina a ejecutar. Pero esto no es cierto... en realidad los eventos son "disparadores" de rutinas. Los eventos se emplean para notificar al usuario (u objeto interesado) de que algo a cambiado en el objeto que lo dispara. ¿Que rutina ejecutará? Aquella rutina que nosotros indicamos cuando hacemos doble clic en evento interesado a capturar.
En realidad el objeto no sabe que rutina va a hacer... simplemente sabe la dirección de la memoria donde está la rutina.
Por si fuera poco, estas rutinas no están obligadas a implementarse. Es decir, que el objeto "notificará" de los "cambios" siempre y cuando uno le asigne una rutina. O en pocas: cuando uno le indique que hacer con dichos "cambios".

Dije que esto se maneja con direcciones de memoria. Para comprender el secreto de ésto necesito ir un paso atrás.
Comenzaré a hablar sobre punteros a funciones.

Punteros a funciones
Un Puntero, no es más que una variable que apunta a una porción de memoria determinada. Todo lo que declaramos en Delphi ocupa memoria. Por ejemplo tenemos una función como ésta:



delphi
  1. interface
  2.  
  3. uses StrUtils;
  4.  
  5. function UnaFuncion(Valor: string): string;
  6.  
  7. implementation
  8.  
  9. function UnaFuncion(Valor: string): string;
  10. begin
  11.   result := reversestring(Valor);
  12. end;



"UnaFuncion" ocupará una dirección xxx en memoria. Ahora, Delphi es un lenguaje fuertemente tipeado, por lo cual podemos declarar muchos tipos. Incluso, podemos hacer tipos de función.
Gracias a los tipo de función es que podemos tener punteros a funciones. ¿Porqué no hacer un puntero a la función anterior? ¿Se puede? ¡Claro!

1. Declaramos en type algo como esto:


delphi
  1. type
  2.   TFuncion = function(Param: string): string;



Como se puede ver, hemos declarado un tipo llamado TFuncion. Este tipo no es más que una función con un parámetro y un valor de salida string. Necesariamente debe coincidir la "declaración" de TFuncion con la función a la que deseamos apuntar.

2. Declaremos dos variables: una para el resultado de la función y otra del tipo TFuncion:



delphi
  1. var texto: string;
  2.     Fx: TFuncion;



3. Ahora podemos hacer algo esto:



delphi
  1. begin
  2.   // Asignamos a Fx la función "UnaFuncion"
  3.   Fx := UnaFuncion;
  4.   texto := Fx('Un texto');
  5.  
  6.   ShowMessage(texto);
  7. end;



Como queda ilustrado, a Fx le asignamos la función "UnaFunción". Lo que hace el compilador es almacenar en Fx el valor de la memoria de UnaFunción. Si xxx es la dirección de memoria de UnaFunción, en Fx quedará xxx. Por tanto Fx "apunta" a UnaFunción. ¡Listo tenemos un puntero a una función!.

Al ejecutar la función almacenada en Fx, podemos ver que el texto que nos muestra el ShowMessage es "otxet nU".

De modo similar, los eventos son punteros. Punteros a métodos para ser exactos.

Nota: al igual que las funciones, existen los punteros a procedimientos. El principio es el mismo, lo que cambia es que no se trata de función, sino de procedimientos:



delphi
  1. TProcedimiento = procedure(Param: string);



Punteros a métodos
He dicho antes que los eventos son punteros a métodos. Los punteros a métodos son muy similares, se declara un "tipo de función" y se le añade al final "Of Object". Por ejemplo, en Delphi contamos con el TNotifyEvent:



delphi
  1. type
  2.   TNotifyEvent = procedure(Sender: TObject) of object;



¿Le resulta similar a algo? Ejemplos de TNotifyEvent son OnClick y OnActivate. En pocas cualquier evento que vea en los componentes que tenga sólo el parámetro Sender es del tipo TNotifyEvent.

Este tipo de datos almacena la dirección de memoria de la rutina que será asociada al método y además el objeto sobre el cual se aplica (para poder identificarlos del resto).
Los punteros a métodos sólo se aplican en el contexto de un objeto. No se puede hacer uso de ellos en forma "suelta". Deben estar asociados a un objeto.

Probemos entonces con lo aprendido.
1. Declare el siguiente objeto y puntero a método.


delphi
  1. type
  2. //punteros a métodos
  3.   TGritoEvento = procedure(Valor: integer) of Object;
  4.  
  5.   // Una clase que usará los punteros a métodos
  6.   TSujetoLoteria = class(Tobject)
  7.     GritarValor: TGritoEvento;
  8.     CantarValor: TGritoEvento;
  9.     procedure Accion(NroAccion, Valor: integer);
  10.   end;



2. Ahora dele la siguiente implementación a "Accion":



delphi
  1. procedure TSujetoLoteria.Accion(NroAccion, Valor: integer);
  2. begin
  3.   case NroAccion of
  4.   0: if Assigned(GritarValor) then GritarValor(Valor);
  5.   1: if Assigned(CantarValor) then CantarValor(Valor);
  6.   else ShowMessage('Accion invalida');
  7.   end; // case
  8. end;



Como se vé, dependiendo del número de acción se dispará ya sea el evento "GritarValor" o "CantarValor" con el valor dado por parámetro.
Si se presta atención, primeramente, se evalúa si GritarValor o CantarValor apuntan a una dirección de memoria válida (esto se hace con Assigned).

3. Ahora nos falta implementar las rutinas que queremos que se realizen cuando "grite" o "cante". Implemente las siguientes rutinas como métodos de algun form (o si desea como métodos de la misma clase TSujetoLoteria):



delphi
  1. procedure TForm1.RutinaGrito(Valor: integer);
  2. begin
  3.   ShowMessage('EL NUMERO ES ' + IntToStr(Valor));
  4. end;
  5.  
  6. procedure TForm1.RutinaCanto(Valor: integer);
  7. begin
  8.   ShowMessage('El número de hoy es ' + IntToStr(Valor));
  9. end;



Yo lo hice dentro de un TForm para demostrar que no necesariamente la rutina debe estar en la misma clase. De hecho lo normal y esperado es que éstas rutinas sean ajenas a la clase. Recuerde que buscamos que el objeto nos "avise" de los cambios, el no sabe donde están las rutinas.

4. Ahora coloque un botón y realice lo siguiente:



delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var TChico: TSujetoLoteria;
  3. begin
  4.   //creamos al objeto
  5.   TChico := TSujetoLoteria.Create;
  6.  
  7.   // asignamos las rutinas a hacer en los eventos
  8.   TChico.GritarValor := RutinaGrito;
  9.   TChico.CantarValor := RutinaCanto;
  10.  
  11.   TChico.Accion(0,200); // Mensaje!
  12.   TChico.Accion(1,40);  // Mensaje!
  13.  
  14.   // liberamos todo
  15.   TChico.GritarValor := nil;
  16.   TChico.CantarValor := nil;
  17.   TChico.Free;
  18. end;



Como puede verse, hemos hecho la asignación de las rutinas a los eventos. GritarValor y CantarValor apuntarán a la dirección de memoria de los métodos RutinaGrito y Rutinacanto respectivamente.
Pruebe el código, deberá recibir dos mensajes, uno con el "grito" de 200 y otro con el "canto" de 40.

¿Que hicimos? básicamente hemos declarado dos rutinas, que se asociarán a dos eventos que nos ofrece a disposición la clase TSujetoLoteria.

Ahora comente la asignación de RutinaGrito y pruebe nuevamente.


delphi
  1. // asignamos las rutinas a hacer en los eventos
  2.   //TChico.GritarValor := RutinaGrito;
  3.   TChico.CantarValor := RutinaCanto;



¿Que sucede? Sólo recibe el mensaje de canto. ¿Porqué? Porque no se le asignó rutina al evento. Como he dicho: los eventos sólo se disparan si tienen una rutina asociada a ellos. Cuando da doble click sobre el OnClick de un botón está "creando" una respuesta (método) para dicho evento. Por tanto, internamente OnClick apuntará a la dirección de memoria del método Button1Cick (por ejemplo).

Bueno, en el proximo post continuaré explicando el tema de los métodos. Ya este post se hizo largo.
  • 0

#2 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 19 December 2008 - 04:02 PM

Notificando cambios

Bueno, continuando con el tema.
Como se ha venido diciendo, los eventos se emplean para comunicar que algo en un objeto ha cambiado. En el ejemplo anterior no se ha cambiado nada. Simplemente hay un método que envía los mensajes a los eventos... fuera de eso el objeto no experimenta cambio alguno.

¿Recuerda el ejemplo del ratón y la escoba? Que tal di dejamos que sea el mismo ratón quien nos haga saber cuanta vida le queda en vez de estar pendientes nosotros mismos?

Primeramente modifiquemos al ratón de este modo:


delphi
  1. TRaton = class(TMamifero)
  2.     Vida: integer;
  3.     X,Y: integer;  // ubicación del ratón
  4.     MaxX, MaxY: integer; // para el tamaño del mapa
  5.  
  6.     // eventos
  7.     AlLastimarse: TEventoLastimarse;
  8.     AlMorirse: TEventoLastimarse;
  9.     AlMoverse: TEventoMoverse;
  10.  
  11.     // métodos
  12.     function VerCueva(var X,Y: integer): boolean;
  13.     procedure Moverse(HaciaX, HaciaY, Velocidad: integer);
  14.     procedure Lastimar(Danno: integer);
  15.     procedure Escapar;
  16.   end;



Ahora declare los siguientes punteros a métodos:


delphi
  1. // definimos los punteros a métodos para el raton
  2.   TEventoLastimarse = procedure(Danno, Vida: integer) of object;
  3.   TEventoMoverse = procedure(X,Y: integer) of object;



La implementación ahora de los métodos tanto del ratón como de la escoba es como sigue:


delphi
  1. { TRaton }
  2.  
  3. procedure TRaton.Escapar;
  4. var X,Y: integer;
  5.     Ver: boolean;
  6. begin
  7. Ver := VerCueva(X,Y);
  8. if Ver
  9.     then Moverse(X,Y,3); // Mensaje! // velocidad 3: muy rápido
  10. end;
  11.  
  12. procedure TRaton.Lastimar(Danno: integer);
  13. begin
  14.   if Vida >= Danno
  15.     then begin
  16.             dec(Vida,Danno);
  17.             // cuando se lastima lanza el evento
  18.             if Assigned(AlLastimarse)
  19.               then AlLastimarse(Danno,Vida);
  20.           end
  21.     else begin
  22.             // se muere! :(
  23.             Vida := 0;
  24.             if Assigned(AlMorirse)
  25.               then AlMorirse(Danno,Vida);
  26.           end;
  27. end;
  28.  
  29. procedure TRaton.Moverse(HaciaX, HaciaY, Velocidad: integer);
  30. begin
  31.   // por ahora velocidad la ignoramos
  32.   X := HaciaX;
  33.   Y := HaciaY;
  34.  
  35.   // avisamos que el ratón se movió
  36.   if Assigned(AlMoverse)
  37.     then AlMoverse(X,Y);
  38. end;
  39.  
  40. function TRaton.VerCueva(var X, Y: integer): boolean;
  41. begin
  42.   // OJO es un ejemplo!
  43.   result := true;
  44.  
  45.   // que la posición de la cueva sea al azar
  46.   X := random(MaxX);
  47.   Y := random(MaxY);
  48. end;
  49.  
  50. { TEscoba }
  51.  
  52. procedure TEscoba.Golpear(Fuerza: integer);
  53. begin
  54.   Victima.Lastimar(Fuerza div 2);
  55.   Victima.Escapar;
  56. end;



Ahora, en la unidad del form1, coloque un botón, y un label. Modifique dicha unit de modo que quede de éste modo:



delphi
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     Button1: TButton;
  12.     Label1: TLabel;
  13.     procedure FormCreate(Sender: TObject);
  14.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  15.     procedure Button1Click(Sender: TObject);
  16.   private
  17.     { Private declarations }
  18.   public
  19.     { Public declarations }
  20.  
  21.     // nuestras rutinas para los eventos
  22.     procedure RutinaLastimar(Danno, Vida: integer);
  23.     procedure RutinaMorir(Danno, Vida: integer);
  24.     procedure RutinaMover(X,Y: integer);
  25.   end;
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30. implementation
  31.  
  32. uses Unit2; // en realidad el nombre de la unit donde tiene declarados las clases escoba y raton!
  33.  
  34. var Escoba: TEscoba;
  35.     Raton: TRaton;
  36.  
  37. {$R *.dfm}
  38.  
  39. procedure TForm1.FormCreate(Sender: TObject);
  40. begin
  41.   // creamos los objetos y establecemos algunos valores iniciales
  42.   Escoba := TEscoba.Create;
  43.   Raton := TRaton.Create;
  44.  
  45.   Escoba.Victima := Raton;
  46.   Raton.Vida := 1000;
  47.   Raton.X := 300;
  48.   Raton.Y := 200;
  49.  
  50.   // le indicamos el tamaño del mapa
  51.   Raton.MaxX := Form1.Width - Button1.Width - 30;
  52.   Raton.MaxY := Form1.Height - Button1.Height - 30;
  53.  
  54.   // le asociamos las rutinas al evento
  55.   Raton.AlLastimarse := RutinaLastimar;
  56.   Raton.AlMorirse := RutinaMorir;
  57.   Raton.AlMoverse := RutinaMover;
  58.  
  59.   // Nuestro botón hará de ratón!
  60.   Button1.Left := Raton.X;
  61.   Button1.Top := Raton.Y;
  62.   Button1.Caption := 'Ratón';
  63.   Label1.Caption := '0/1000';
  64.  
  65. end;
  66.  
  67. procedure TForm1.RutinaLastimar(Danno, Vida: integer);
  68. begin
  69.   Label1.Caption := IntToStr(Danno) + '/' + IntToStr(Vida);
  70. end;
  71.  
  72. procedure TForm1.RutinaMorir(Danno, Vida: integer);
  73. begin
  74. ShowMessage('¡Me muero!');
  75. RutinaLastimar(Danno,Vida);
  76.  
  77. Raton.Vida := 1000;
  78. RutinaLastimar(0,1000);
  79. end;
  80.  
  81. procedure TForm1.RutinaMover(X, Y: integer);
  82. begin
  83.   Button1.Left := X;
  84.   Button1.Top := Y;
  85. end;
  86.  
  87. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  88. begin
  89.   Escoba.Free;
  90.  
  91.   Raton.AlLastimarse := nil;
  92.   Raton.AlMorirse := nil;
  93.   Raton.AlMoverse := nil;
  94.   Raton.Free;
  95. end;
  96.  
  97. procedure TForm1.Button1Click(Sender: TObject);
  98. begin
  99.   Escoba.Golpear(random(100)+50);
  100. end;
  101.  
  102. end.



Como puede verse en el código, la clase TRaton está diseñada para disparar los eventos "Alxxx". Puede declarar otras rutinas para dar otras respuesta a los eventos, o incluso probar como actua cuando comenta las asignaciones.

Puede notar ahora como dado algunos cambios internos en una clase, se disparan eventos que le pueden ser de utilidad al programador según que se modifique.

Continuaremos hablando más adelante de los métodos.
  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 19 December 2008 - 04:10 PM

Muy bien amigo Delphius, estas dando mucho empuje al foro, muchas gracias

Salud OS
  • 0

#4 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4483 posts
  • LocationVenezuela

Posted 19 December 2008 - 04:15 PM

porque no recompilas eso y haces un manual completo de POO ya vemos que eso te apasiona
  • 0

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 19 December 2008 - 04:16 PM

porque no recompilas eso y haces un manual completo de POO ya vemos que eso te apasiona


No seria mala idea que se juntaran los hilos en uno solo en órden de temas.

Salud OS
  • 0

#6 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4483 posts
  • LocationVenezuela

Posted 19 December 2008 - 04:18 PM

solo que no le pongan en la portada al chigüire con pico de pato ese  ;)
  • 0

#7 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 19 December 2008 - 04:25 PM

Muy bien amigo Delphius, estas dando mucho empuje al foro, muchas gracias

Salud OS

No, gracias a ustedes, y a este lindo lugarcito. :)

porque no recompilas eso y haces un manual completo de POO ya vemos que eso te apasiona

Lo estuve pensando, pero se me hace que puede ser productivo hacerlo por partes. De modo que puede seguirse el tema en forma pausada, gradual. Además me obliga a ser lo menos técnico posible. Pero lo más importante: otras personas pueden enriquecer los temas.

Si hago uno entero sobre POO lo hago en .doc y luego lo publico. Y si es conveniente lo convierto a pdf con una impresora pdf. La cuestión es que si hago un "libro" completo sobre POO (incluso si sumo patrones) prefiero hacerlo algo más técnico.

Me agarraron inspirado estos días, y bueno... le metí 5ta. :p :D :)

Saludos,

  • 0

#8 FGarcia

FGarcia

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 687 posts
  • LocationMéxico

Posted 20 December 2008 - 09:58 AM

No habia leido esta serie de hilos sobre herencia, polimorfismo y todo lo de POO. Muy Buenos y se agradece la sencillez.

Delphius disculpa que critique (mas si no se gran cosa de esta tema) pero es precisamente esa sencillez lo que hace agradable leer un libro o manual sobre cualquier tema y si ademas se le pone un poco de humor que mejor. Para manuales con datos tecnicos el internet y las librerias (si, esas donde venden libros -por si las dudas-) estan llenos y la verdad siempre los deja uno a las primeras 5 paginas; por otro lado siempre estan al final de los capitulos las referencias bibliograficas por si alguien quiere empaparse de tecnicismos. Me inclino a que no sientas pena por hacer algo tan educativo de una manera tan sencilla. Gracias y saludos a todos los del foro.
  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 20 December 2008 - 01:22 PM

No habia leido esta serie de hilos sobre herencia, polimorfismo y todo lo de POO. Muy Buenos y se agradece la sencillez.

Delphius disculpa que critique (mas si no se gran cosa de esta tema) pero es precisamente esa sencillez lo que hace agradable leer un libro o manual sobre cualquier tema y si ademas se le pone un poco de humor que mejor. Para manuales con datos tecnicos el internet y las librerias (si, esas donde venden libros -por si las dudas-) estan llenos y la verdad siempre los deja uno a las primeras 5 paginas; por otro lado siempre estan al final de los capitulos las referencias bibliograficas por si alguien quiere empaparse de tecnicismos. Me inclino a que no sientas pena por hacer algo tan educativo de una manera tan sencilla. Gracias y saludos a todos los del foro.

Muchas veces vienen las ganas de escribir sobre POO pero la cuestión es que no me resulta tan fácil salir del ambiente técnico. En algunos conceptos es más fácil, otros son más pesados.
El humor si, es necesario para no aburrirlos... tal vez si elijo ejemplos como el de la escoba y el ratón sea más fáciles de reir.
Cuando termine de redactar algunos post con conceptos que me quedan en la galera, los voy a reunir y presentar hermosamente (con ejemplos sencillos y humoristicos en lo posible) en un documento para que quede a disposición de la comunidad. ¿Se podría preveer un espacio donde podamos poner documentos?
  • 0

#10 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 27 December 2008 - 12:21 PM

Bueno, continuando con el tema, hemos visto como mediante código le podemos asignar un evento a un objeto. Básicamente se sigue la siguiente regla:

Objeto.OnEvento := RutinaEvento;

Donde RutinaEvento es algún método que tiene idéntica declaración de parámetros, en tipo, cantidad y orden que necesita el puntero a método. De otro modo no se puede hacer una correcta asignación de eventos.

Ahora es necesario conocer dos conceptos importantes: emisor y receptor.

Emisor y receptor

El emisor es el objeto que dispara el evento.
El receptor es por tanto el método de algún objeto (no necesariamente debe ser el mismo).

¿Vieron que sucede cuando uno crea una respuesta al evento OnClick de un botón? Uno se topa con algo como esto:



delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.  
  4. end;



Una preguntita ¿Porqué el método está declarado como parte de TForm1 y no el botón?
Una respuesta rápida nos lleva a decir: "Asi funciona Delphi". Asi es, así funciona... pero completemos la respuesta:
En realidad, lo que estamos indicando es que el emisor (el TButton) tiene algo para decir, y se lo hacemos saber al TForm al que pertenece mediante un método (el receptor). Por ello es que automáticamente se crean los métodos como parte del TForm: ¿Qué sentido tendría que un botón se hiciera saber a si mismo que se ha pulsado en él?
La idea es:
El emisor le indica a un método de otro objeto que algo ha sucedido y necesita saber que hacer con ello. Luego, verá el objeto emisor que más debe hacer dependiendo de las circunstancias.

En el caso de que asignemos un evento en tiempo de ejecución (por código), estamos indicando básicamente esto:



delphi
  1. Emisor.OnEvento := Receptor;



Es decir, le hacemos saber al objeto emisor, hacia que método receptor "apuntar".

El parámetro Sender
¿No se preguntaron además para que sirve ese parámetro que suele aparecer tantas veces en los métodos que implementamos cuando damos respuesta a los eventos?

La respuesta: para identificar al objeto emisor que disparó el evento.
Muchos objetos diferentes pueden apuntar a un mismo método. Por tanto, si tenemos un método como respuesta a diferentes eventos de dos o más objetos y necesitamos identificar a uno de otro ¿de que otro modo lo hacemos?

Sender existe por algo fundamental: Poder compartir los eventos. Sino haga la prueba: cree una respuesta a un OnClick de un botón. Luego coloque otro botón, y esta vez en vez de crearlo, seleccione el evento Button1Click de la lista. De este modo ambos objetos apuntarán al mismo método. Como implementación del método haga lo siguiente:



delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   if (TButton(Sender).Name = 'Button1')
  4.     then ShowMessage('Se pulsó el Botón 1')
  5.     else ShowMessage('¡Magia! Se ha pulsado el botón 2 y llego al mismo lugar');
  6. end;



Cuando presione ambos botones, se mostrará uno u otro cuado de diálogo. Pruebe jugar con diversos controles que tengan el mismo tipo de evento.

Ahora la pregunta interesante: ¿Puede un mismo objeto desde diferentes eventos apuntar a un mismo receptor? Si. De poder se puede, pero el problema es que en este caso "Sender" es el mismo y no hay manera de distinguir que evento fue el que se disparó.

Hasta ahora hemos visto entonces como declarar nuestros propios eventos, asociarlos en forma de ejecución, o en diseño y cuáles son las partes que intervienen.
No se me aburran, que todavía queda algo más para finalizar: los tipos de eventos.

Saludos,
  • 0

#11 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 27 December 2008 - 12:47 PM

Para finalizar el tema de los eventos, veamos los tipos de eventos.

Tipos de eventos

Básicamente hay tres motivos por los cuales se disparan los eventos:
1. El emisor ha sufrido un cambio interno y quiere avisarnos de ello. Es lo que ha se venido indicando hasta el momento.
2. El usuario ha realizado alguna acción. Como por ejemplo una pulsación de tecla, un click, etc. En este caso, Windows notifica al objeto, y el objeto no los hace saber mediante un evento. Y por último,
3. Internamente el emisor necesita de nuestra intervención u opinión antes de continuar con algún algoritmo. Un ejemplo es el caso del evento OnCloseQuery.

Veamos y repasemos un poquito esto:
Los del tipo 1 son muy comunes en aquellos componentes no visuales. Un ejemplo de ésto lo hemos visto en la clase TRaton cuando hacíamos esto:



delphi
  1. if (Assigned(AlLastimarse) then AlLastimarse(Danno, Vida)



El objeto nos hace saber que algo se modificó disparando el evento adecuado.

Los del tipo 2 son los más comunes. Ejemplos de éstos encontramos a diario... OnKeyDown, OnMouseDown, OnKeyPress, etc. Tiene muchos parámetros para indicarnos que ha sucedido. Es normal que estos eventos disparen a su vez otros eventos.
También deben añadirse a la lista los eventos relacionados a mensajes que produce Windows: OnClose, OnActivate, OnResize.
En pocas: los eventos 2 son aquellos que se encargan de detalles más relacionados con el manejo y la comunicación con el sistema operativo.

Los del tercer tipo son aquellos en donde se encuentran los parámetros por referencia. El objeto emisor necesita de este parámetro para pedirnos que decidamos que debe o no debe hacer. En pocas sucede algo como "Hey mira... ha sucedido esto... ¿Que te parece? Dime si hago esto o aquello"

En el evento OnCloseQuery contamos con el parámetro CanClose, inicialmente esta valor vale True... pero el emisor nos deja la posibilidad de que decidamos y modifiquemos este valor. Al finalizar el evento, dependiendo del valor final de CanClose el emisor hará una u otra cosa.
Lo que hace el objeto es algo así:



delphi
  1. CanClose := True;
  2. if Assigned(FOnCloseQuery) then FonCloseQuery(Self, CanClose) // dispara el evento...
  3. if CanClose then ...



Deja que decidamos el flujo de acción. Dispara el evento y luego chequea el valor para saber que hacer.

Bueno aqui finalizo el tema de los eventos. Si tienen dudas, no teman. Explico nuevamente.

Saludos,
  • 0

#12 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2092 posts
  • LocationRepública Dominicana

Posted 06 January 2009 - 07:39 AM

Lastima haber llegado tarde por estos lados, pero me parecen muy buenas las aportaciones Delphius .

Les echare una mirada.
  • 0

#13 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 06 January 2009 - 08:28 AM

Lastima haber llegado tarde por estos lados, pero me parecen muy buenas las aportaciones Delphius .

Les echare una mirada.

Hola RolphyReyes,
Muchas gracias.
Puedes seguir aportando en lo que te sientas cómodo. Cualquier observación, o ampliación a estos manuales y tutoriales que consideres oportuna es bienvenida.
Si bien otros compañeros y yo iniciamos esta sección, el objetivo es que toda la comunidad se sienta cómoda y pueda aportar con su experiencia.

Saludos,
  • 0




IP.Board spam blocked by CleanTalk.