Un Manejador de Eventos Para VB6 – Parte 2

Una clase que maneje los eventos de componentes ActiveX puede ir mas allá de la directiva Event

Por Harvey Triana

 

Introducción

 

En la primera parte de este articulo di a conocer las bondades de un manejador de eventos para VB6, y mostré como se pueden implementar colecciones de objetos que generen eventos. En esta parte trataré el cómo se pueden lanzar eventos desde un formulario u objeto que fue cargado desde la interfaz Form u Object.

 

Eventos desde un Objeto Form

 

Una de las grandes bondades de un objeto Form, es que podemos abrir cualquier formulario apuntando su referencia a un objeto Form, es así como podemos lanzar un formulario a partir de una cadena de texto. Esto es supremamente útil para reutilizar código de formularios desde clases. Esta técnica la he usado en muchas aplicaciones,  y créame que es muy practico lanzar un formulario de datos en una línea de código, y dejar que una clase configure todo automáticamente. Voy a exponer un ejemplo sencillo, para vislumbrar la técnica.

 

En primera instancia voy a presentar la solución en donde los detalles de la Clase CEventsHandler se ocultan al cliente. Luego expondré el caso en donde la clase CEventsHandler es publico y debe pasarse a través de un método constructor desde el cliente. Ambas estrategias tienen sus propias bondades.

 

Digamos que tenemos una clase que se llama CFormDataAccess, la cual se emplea para envolver formularios genéricos de datos dentro de un componente. Esta clase abre el formulario desde su nombre, sin crear la clase explicita del formulario en sí (para esto se requiere de un objeto Form y de Forms.Add(FormName)). Luego configura el formulario según los parámetros pasados. Para seguir el ejemplo cree una DLL ActiveX, agregue la clase CEventsHandler (el código se encuentra en la primera parte de este articulo). El código de la clase CFormDataAccess es el siguiente:

 

'//==============================================================

'// NAME        : CFormsDataAccess

'// AUTHOR      : Harvey Triana (harvey@geocoweb.net)

'// DESCRIPTION : Corte de código para ilustrar el ejemplo del

'//               articulo "Un Manejador de Eventos Para Visual

'//               Basic Escrito en Visual Basic - Parte 2

'//               CEventsHandler es oculto al cliente

'//==============================================================

Option Explicit

 

Public Event Apply()

 

Private WithEvents DataForm As Form

 

'//EH Implementación

Private WithEvents EventsHandler As CEventsHandler

 

Public Function OpenForm( _

    FormName As String, _

    ParamArray Params() As Variant)

 

    '// crea la instancia del formulario

    Set DataForm = Forms.Add(FormName)

 

    '// Detalles de carga en el formulario

    '// ...

    '// configura el formulario según parámetros en Param()...

    '// ...

 

    '//EH Implementación

    On Error Resume Next

    Set DataForm.EventsHandler = EventsHandler

    On Error GoTo 0

   

    '//muestra el dialogo

    DataForm.Show vbModal

    Unload DataForm

End Function

 

Private Sub Class_Initialize()

    '//EH Implementación

    Set EventsHandler = New CEventsHandler

End Sub

 

Private Sub Class_Terminate()

    '//EH Implementación

    Set EventsHandler = Nothing

End Sub

 

'//EH Implementación

Private Sub EventsHandler_RaiseEvents( _

    Sender As Object, _

    EventName As String, _

    EventArgs As Variant)

 

    Select Case EventName

        Case "Apply"

             RaiseEvent Apply

    End Select

End Sub

'//==============================================================

 

Las líneas de código que gestionan CEvensHandler aparecen en color violeta, y precedidas por el comentario EH Implementación. Como el código anterior oculta los detalles al cliente del manejador de eventos, se escribió un evento explicito de nombre Apply (para ilustrar la técnica). El problema que se debía resolver era el siguiente: “Como lanzar el evento Apply (o cualquier otro) al cliente, si el formulario se cargo desde un objeto Form. Pues bien, la clase CEventsHandler ha resuelve esto.

 

Para completar el ejemplo, agregue a la DLL un formulario, con un botón Apply. El nombre del Form será frmDialog1. El código del formulario frmDialog1 es el siguiente:

 

'//==============================================================

'// NAME        : frmDialog1

'// AUTHOR      : Harvey Triana (harvey@geocoweb.net)

'// DESCRIPTION : Corte de código para ilustrar el ejemplo del

'//               articulo "Un Manejador de Eventos Para Visual

'//               Basic Escrito en Visual Basic - Parte 2

'//               CEventsHandler es oculto al cliente

'//==============================================================

Option Explicit

 

'//EH Implementación

Private m_EventsHandler As CEventsHandler

 

'//EH Implementación

Public Property Set EventsHandler(v As CEventsHandler)

    Set m_EventsHandler = v

End Property

 

Private Sub cmdApply_Click()

    '//EH Implementación

    m_EventsHandler.Trigger Me, "Apply", Empty

End Sub

 

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

    If UnloadMode = vbFormControlMenu Then

       Cancel = True

       Me.Hide

    End If

End Sub

 

Private Sub Form_Unload(Cancel As Integer)

    Set m_EventsHandler = Nothing

End Sub

'//==============================================================

 

El código luce algo extenso dados los detalles de EventsHandler, pero vale la pena, puesto que así como podemos lanzar un evento Apply, podemos lanzar muchos otros; en este caso tendrías que escribir código para dar los nombre explícitos en la clase CFormDataAccess, - este el precio de ocultar EventsHandler al cliente. Veamos el código del cliente (un EXE estándar y agregue el proyecto anterior en un grupo de proyectos):

 

'//==============================================================

'// NAME        : Form1

'// AUTHOR      : Harvey Triana (harvey@geocoweb.net)

'// DESCRIPTION : Corte de código para ilustrar el ejemplo del

'//               articulo "Un Manejador de Eventos Para Visual

'//               Basic Escrito en Visual Basic - Parte 2

'//==============================================================

Option Explicit

 

Private WithEvents di As CFormDataAccess

 

'//muestra del formulario "frmDialog1" encapsulado en el

'//componente, es decir alguna DLL u OCX

Private Sub Command1_Click()

    Set di = New CFormDataAccess

    di.OpenForm "frmDialog1", "TEST0001", 1, True, "etc."

    Set di = Nothing

End Sub

 

Private Sub di_Apply()

    MsgBox "Fue lanzado el evento Apply"

End Sub

'//==============================================================

 

El formulario del cliente no sabe nada de CEventsHandler, luce limpio y simple. Sin embargo es más labor para el que escribe el componente.

 

Ahora bien, como se enuncio inicialmente, podemos hacer publica la clase CEventsHandler, crear una referencia explicita en el cliente, pasarla a la clase CDataFormAccess, y esta a su vez la pasa al frmDialog1. Esta estrategia hace más versátil el código de Eventos, pero hace estricto el código para el cliente. Bien, podemos mitigar esto al agregar un Constructor a CDataFormAccess, con EventsHandler como parámetro. Nota. VB6 no acepta constructores, pero es simple lograrlo si se sigue la regla de crear un método publico Constructor, y como regla (contrato) siempre llamarlo después de crear el objeto. Los tres módulos bajo esta estrategia lucen así:

 

Componente:

 

'//==============================================================

'// NAME        : CFormsDataAccess

'// AUTHOR      : Harvey Triana (harvey@geocoweb.net)

'// DESCRIPTION : Corte de código para ilustrar el ejemplo del

'//               articulo "Un Manejador de Eventos Para Visual

'//               Basic Escrito en Visual Basic - Parte 2

'//               La clase EventsHandler es publica

'//==============================================================

Option Explicit

 

Private DataForm As Form

 

'//EH Implementación

Private m_EventsHandler As CEventsHandler

 

Public Sub Constructor(pEventsHandler As CEventsHandler)

    Set m_EventsHandler = pEventsHandler

End Sub

 

Public Function OpenForm( _

    FormName As String, _

    ParamArray Params() As Variant)

   

    '// crea la instancia del formulario

    Set DataForm = Forms.Add(FormName)

 

    '// Detalles de carga en el formulario

    '// ...

 

    '// configura el formulario según parámetros en Param()...

    '// lo que sea, no importa

    '...

 

    '//EH Implementación

    On Error Resume Next

    Set DataForm.EventsHandler = m_EventsHandler

    On Error GoTo 0

   

    '//muestra el dialogo

    DataForm.Show vbModal

    Unload DataForm

End Function

 

Private Sub Class_Terminate()

    '//EH Implementación

    Set m_EventsHandler = Nothing

End Sub

'//==============================================================

 

Formuladio envuelto en el componente:

 

'//==============================================================

'// NAME        : frmDialog1

'// AUTHOR      : Harvey Triana (harvey@geocoweb.net)

'// DESCRIPTION : Corte de código para ilustrar el ejemplo del

'//               articulo "Un Manejador de Eventos Para Visual

'//               Basic Escrito en Visual Basic - Parte 2

'//               La clase EventsHandler es publica

'//==============================================================

Option Explicit

 

'//EH Implementación

Private m_EventsHandler As CEventsHandler

 

'//EH Implementación

Public Property Set EventsHandler(v As CEventsHandler)

    Set m_EventsHandler = v

End Property

 

Private Sub cmdApply_Click()

    '//EH Implementación

    m_EventsHandler.Trigger Nothing, "Apply", Empty

End Sub

 

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

    If UnloadMode = vbFormControlMenu Then

       Cancel = True

       Me.Hide

    End If

End Sub

 

Private Sub Form_Unload(Cancel As Integer)

    '//EH Implementación

    Set m_EventsHandler = Nothing

End Sub

 

Cliente:

 

'//==============================================================

'// NAME        : Form1

'// AUTHOR      : Harvey Triana (harvey@geocoweb.net)

'// DESCRIPTION : Corte de código para ilustrar el ejemplo del

'//               articulo "Un Manejador de Eventos Para Visual

'//               Basic Escrito en Visual Basic - Parte 2

'//               La clase EventsHandler es publica

'//==============================================================

Option Explicit

 

'//EH Implementación

Private WithEvents EventsHandler As CEventsHandler

 

Private Sub Command1_Click()

    Dim di As CFormDataAccess

 

    Set di = New CFormDataAccess

    '//EH Implementación

    Set EventsHandler = New CEventsHandler

   

    '//EH Implementación

    di.Constructor EventsHandler

   

    di.OpenForm "frmDialog1", "TEST0001", 1, True, "etc."

    '//nota. Los parametros que "TEST0001", 1, True, "etc."

    '//son inventados yno tienen relevancia

    Set di = Nothing

   

    '//EH Implementación

    Set EventsHandler = Nothing

End Sub

 

'//EH Implementación

Private Sub EventsHandler_RaisedEvent( _

    Sender As Object, _

    EventName As String, _

    EventArgs As Variant)

   

    Select Case EventName

        Case "Apply"

              MsgBox "Fue lanzado el evento Apply"

    End Select

End Sub

'//==============================================================

 

Si Usted es un programador versado en VB6, notara inmediatamente las ventajas de la segunda estrategia frente a la primetra. Primero, no tenemos que declarar en la seccion declaraciones el objeto de FormDataAccess. Segundo, no tenemos que delcarar eventos de   FormDataAccess. Tercero, el numero de eventos y sus parametros son ilimitados (no afectamos la librería de tipos del componente). Cuarto, solo existe un WithEvents en todo el proyecto.

 

Es importante anotar algo. Dado que el evento lo produce el Boton Apply del formulario envuelto en la clase, no debemos ejecutar el metodo Trigger pasando Me en Sender. Esto produciria peligrosas referencias circulares. Si dese pasar algo de la informacion del formulario, pasela en EvensArg, ya que al fin y alcabo es una Variant y acepta cualquier fuente de datos. Si desea pasar un Recordset al cliente, paselo como Clone y desconectado.

 

 

Conclusiones

 

La clase EventsHandler impone un potente mecanismo para transferencia de eventos en VB6 y de la escritura de los mismos. La técnica luce avanzada para cualquier programador medio. El poder de VB6 esta en saber programar objetos y saberlos usar.