30 de marzo de 2016

BindEvent(), RaiseEvent() y UnbindEvents()

Título original: BindEvent(), RaiseEvent(), and UnbindEvents()
Autor: Nancy Folsom
Traducido por: Luis María Guayán


Varios autores han escrito respecto de estas funciones en varias publicaciones desde que las funciones fueron introducidas en la versión 8.0 de Visual FoxPro. En FoxTalk, escribí sobre cómo ellas pueden ser utilizadas para sustituir lo que habían sido las tortuosas notificaciones de objeto editor-suscriptor. En particular, el artículo (EventBinding2002_NancyFolsom.pdf) muestra como BindEvent simplifica la interacción entre las capas lógicas de negocio y de presentación.

BindEvent

Use BindEvent para decirle a VFP que cuando ejecute el Método "A", también ejecute el Método "B". Por ejemplo, los botones son un gran medio para los usuarios, para dar a conocer a la computadora que ellos quieren que algo suceda, como guardar datos, ejecutar un informe o preparar una rica taza de té. Podemos, y la mayoría lo tenemos, poner el código para hacer cualquier tarea que se quiera, en el método Click o en cualquier otro objeto que el método Click requiera.

La cosa buena sobre BindEvent consiste en que ahora realmente no hay ninguna razón, jamás, de tener ningún código en un método Click de un botón. Nunca. El botón puede ser mudo como un poste. Si usted pega el siguiente fragmento de código en un archivo PRG y lo ejecuta, usted debería ver el formulario mostrado en la Figura 1.

PUBLIC oForm1
oForm1 = NEWOBJECT("Form1")
RETURN
DEFINE CLASS Form1 AS FORM
  HEIGHT = 90
  WIDTH = 330
  CAPTION = "¿Puede Ud. mantener la ansiedad?"
  NAME = "Form1"
  ADD OBJECT Command1 AS COMMANDBUTTON WITH ;
    TOP = 30, ;
    LEFT = 120, ;
    HEIGHT = 27, ;
    WIDTH = 80, ;
    CAPTION = "No!!!", ;
    NAME = "Command1"
  PROCEDURE INIT
    * Cuando el usuario hace clic en el botón, se
    * dispara el método QueryUnload() del formulario
    BINDEVENT(THISFORM.Command1, 'Click', THISFORM, 'QueryUnload')
    THIS.SHOW()
  ENDPROC
  PROCEDURE QUERYUNLOAD
    THISFORM.RELEASE()
  ENDPROC
ENDDEFINE

Figura 1: Click del botón enlazado al método QueryUnload

El ejemplo no parece muy impresionante ya que podríamos haber llamado simplemente al método QueryUnload desde el método Click del botón de comandos. Sin embargo, esto es efectivamente impresionante porque demuestra algunos conceptos importantes:

  1. BindEvent ahorra escritura de código, y parafraseando al difunto Ed Rauh, menos código que escribo, menos errores (bugs) tengo.
  2. El botón no es especial (excepto el título, quizás) y entonces BindEvents simplifica la jerarquía de objeto.
  3. El objeto que se ocupa por el Click del botón es el mismo responsable de resolver que hacer en caso del evento Click.

Si comenzará a usar BindEvent en situaciones sencillas como ésta, pronto descubrirá que esto no lleva a situaciones mucho más complejas para demostrar que vale la pena.

Vale la pena decir que la función BindEvent no ejecutó ningún código en ninguno de los dos métodos. También vale la pena indicar que cualquier número de objetos puede enlazar a un método. De este modo, por ejemplo, un botón Guardar, un botón Salir, un botón Agregar y una Cuadrícula, pueden estar todos, supongamos, enlazados a un método Guardar en un objeto de negocio. Finalmente podemos enlazar a propiedades además de métodos.

¿Qué ejecuta y cuando ejecuta?

BindEvent tiene un quinto parámetro opcional que determina qué código del método se ejecuta primero. Aquí está otra vez la línea BindEvent:

BindEvent(ThisForm.command1, 'Click', ThisForm, 'QueryUnload')

En primer lugar, en la jerga del archivo de ayuda, el Click del objeto Command1 es el evento y el QueryUnload es el delegado. De este modo, en los parámetros. El quinto parámetro, nflags, es 0 por omisión. Los valores posibles son los siguientes:

Pasando 0 (el valor por omisión) significaría que QueryUnload (el delegado) ejecutará antes el método Click (el evento). Puesto que no tengo ningún código en el método Click, este está perfecto.

Pasando 1 significaría que se dispararía el método Click y luego el método QueryUnload.

Pasando 2 significaría que el QueryUnload no se ejecutaría si un código llama al método Click del botón (expira, es la idea!). De este modo, el método de QueryUnload no se ejecutaría como respuesta al siguiente código:

ThisForm.Command1.Click()

Pasando 3 se combina 1 y 2 (ya que este es un parámetro de bit aditivo).

RaiseEvent()

RaiseEvent() aparenta lo mismo que invocar un método directamente. Sin embargo, hay algunas diferencias importantes. Por ejemplo, supongamos tener una clase Formulario que tiene un método personalizado Guardar. Hemos decidido que cuando el formulario se cierra, cualquier cambio es guardado. Considerando esto, compare:

Procedure QueryUnload
  Local llSaved
  If Thisform.Guardar()
    Thisform.Release()
  Endif
Endproc
y
Procedure QueryUnload
  Local llSaved
  Raiseevent(Thisform,'Guardar')
  Thisform.Release()
Endproc

En el primer ejemplo, la llamada a ThisForm.Guardar() puede no disparar ningún otro método que haya sido enlazado al método Guardar() usando BindEvent al ejecutarse, si el quinto parámetro de BindEvent es 2 ó 3. Sin embargo, el segundo ejemplo RaiseEvent va ha retornar siempre .T., entonces usted no podía usarlo para impedir el cierre del formulario. A veces esto es importante, y a veces no. Mi criterio general es usar RaiseEvent, en vez de un llamado a un método, si no tengo que comprobar el valor devuelto, y si es posible que otros objetos puedan estar enlazados al método que quiero llamar. Es probablemente seguro decir que si un método es un método privado, lógica o físicamente, entonces puede solo llamar al método.

Otro uso para RaiseEvent es para los eventos y métodos nativos que, por si solos, no lanzan un evento VFP cuando son llamados directamente en el código. Aquí el archivo de ayuda es completamente claro en ejemplos, pero, en cuanto lo que se, puedo decir que no hay una lista exhaustiva de métodos y eventos que requerirían un RaiseEvent. En el caso de uno de estos métodos, como Activate, usted puede simplemente lanzar el método desde dentro del método. RaiseEvent aparenta ser capaz de evitar la recursión infinita. De este modo, un método Activate de una forma podría "RaiseEvent" a sí mismo como esta manera:

Procedure Activate 
  RaiseEvent(This,'Activate') 
Endproc 

RaiseEvent permitirá que pase parámetros. Si algún método está enlazado al método que usted lanza, estos tendrán que ser capaces de aceptar parámetros.

RaiseEvent es menos obvio que BindEvent. Por suerte la dos funciones no tienen tanto para hacer necesariamente una con la otra como podría pensar. Entonces, aun cuando no encuentre un uso inmediato para RaiseEvent, trate de incorporar BindEvent tan pronto como pueda.

UnbindEvents()

Desenlace los dos métodos anteriores enlazados conjuntamente con BindEvent. No tiene que desenlazar eventos como algo natural. Sólo desenlace eventos si usted ya no necesita el enlace.

Hay una manera más agradable, con BindEvent y UnbindEvents, de manejar el error que ocurre porque los datos aun no están disponibles para un control, que emplear una combinación Try/Catch para determinar el estado de error, tan pronto como sea posible. Mejor dicho, ¿Cómo un objeto le dice al otro? "Oye, despiérteme cuando tengas tus cosas listas, ¿de acuerdo?" . Pero dejaré esto para otro artículo. La Nota de Referencias es que los enlaces de eventos fueron agregados en Visual FoxPro 8.0. La versión 9.0 agregó la capacidad de enlazar eventos de Windows.

Aquí están algunos enlaces útiles:

Lo invito a que me contacte en nfolsomNOSPAM@NOSPAMpixeldustindustries.com con cualquier comentario, preguntas o crítica. Sus comentarios serán bienvenidos.

Nancy Folsom

2 comentarios :

  1. Hola. El link al documento EventBinding2002_NancyFolsom.pdf esta caido. Serias tan amable de subirlo de nuevo ? Gracias de antemano.

    ResponderBorrar
    Respuestas
    1. Listo, ya está corregido

      https://drive.google.com/file/d/1B7mN8s9UbPjhB49v68-KqrqH8UGj3E-B/view?usp=sharing

      Borrar

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.