Un Manejador de Eventos Para VB6 – Parte 1
Una clase que maneje los eventos de
componentes ActiveX puede ir mas allá de la
directiva Event
Por Harvey Triana
Introducción
Una clase que maneje los eventos de componentes ActiveX puede ir mas allá de la
directiva Event. Las razones son las siguientes:
(1) Se pueden implementar fácilmente colecciones de objetos que generen
eventos, (2) Se pueden lanzar eventos desde un formulario u objeto que
cargado desde la interfaz Form u Object, (3) El cliente puede recibir eventos aun sin la
directiva WithEvents, (4) Puede escribir tantos
eventos como desee o modificarlos sin afectar la librería de tipos del
componente, y por ende la compatibilidad binaria. Quizás habrá otros
servicios que yo no me he imaginado. Cada uno de estos enunciados se explica
con detalle en este articulo.
La Clase CEventsHandler
Tan interesantes enunciados que dan la introducción de
este articulo, se logran a través de la clase CEventsHandler,
he aquí su código:
'//==============================================================
'// NAME : CEventsHandler
'// AUTHOR : Harvey Triana
(harvey@geocoweb.net)
'// DESCRIPTION :
'//==============================================================
Option Explicit
Public Event RaisedEvent( _
Sender As Object, _
EventName
As String, _
EventArgs
As Variant)
Public Sub Trigger( _
Sender As Object, _
EventName As
String, _
EventArgs As
Variant)
RaiseEvent RaisedEvent(Sender, EventName, EventArgs)
End Sub
'//==============================================================
Basta leer el código anterior para vislumbrar la
idea, el objeto dentro del componente hace un llamado al método Trigger para lanzar cualquier evento, dado un nombre y
unos argumentos en una matriz Variant llamada EventsArg. ¿Se le parece algo a VB.NET?, no es
extraño, pues la primera vez que vi como manejaba
los eventos VB.NET, se me ocurrió la idea para VB6, aunque si bien, es
bastante diferente en detalle.
El empleo de la clase CEventsHandler
puede ser laborioso, aunque es grato saber que soluciona con holgura algunas
limitaciones técnicas de WithEvents, y que la
estrategia aplica de la misma forma en todos los casos.
Eventos desde una Colección de objetos
VB6 tiene una forma elegante de manejar objetos en
jerarquía, y es a través de una colección implementada y gobernada por una clase
y clases hijo de esta. Por supuesto no se trata de “herencia” tal como la
define OOP, pero es extremadamente útil para tener ventajas de programación
de objetos con VB6. Si usted ha leído bastante sobre VB6, se acordara de la
casa de ladrillo, a eso me refiero. Sin embargo esta implementación de
jerarquía de objetos encuentra una limitación: ¿Cómo manejar eventos desde
los objetos hijo?. Puesto que no podemos declarar Private WithEvents colAlgo As Collection, o cosa
que se le parezca. He aquí la solución para recibir eventos desde los objetos
hijo. Voy a exponer un ejemplo sencillo, para vislumbrar la técnica.
Digamos que tenemos una clase que se llama CPersons, con dos propiedades básicas, Nombre y Apellido,
y un método que inicia una evaluación en tiempo, por ejemplo la llegada de la
persona a la oficina. El código de la clase CPerson
es el siguiente:
'//==============================================================
'// NAME : CPerson
'// AUTHOR : Harvey Triana (harvey@geocoweb.net)
'// DESCRIPTION :
Ejemplo del articulo vb6EventsHndler
'// www.mvps.org/vexpert
'//==============================================================
Option Explicit
Private m_FirstName As String
Private m_LastName As String
Private WithEvents TimerSample
As Timer
Private f As frmTimer
Public Property Let FirstName(v As String)
m_FirstName
= v
End Property
Public Property Get FirstName() As String
FirstName
= m_FirstName
End Property
Public Property Let LastName(v As String)
m_LastName
= v
End Property
Public Property Get LastName() As String
LastName
= m_LastName
End Property
Private Sub Class_Initialize()
Set f = New frmTimer
Load f
Set TimerSample
= f.tmrControl
End Sub
Private Sub Class_Terminate()
Unload f
End Sub
Public Sub StartSample()
Randomize Timer
TimerSample.Interval
= Rnd * 5000
TimerSample.Enabled
= True
End Sub
Private Sub TimerSample_Timer()
TimerSample.Enabled = False
'//PROBLEMA:
' Se requiere lanzar un evento aquí que
suministre
' el tiempo de llegada de la persona:
' ...
End Sub
'//==============================================================
Se crea una referencia a un proceso Timer,
el cual esta envuelto en un Form. Solo basta
agregar un Form al proyecto, y adicionar un Timer (los nombres respectivamente son: nombre del
formulario: frmTimer; nombre del Timer: tmrControl). Este Timer se
emplea como una muestra para visualizar el comportamiento de eventos por cada
objeto de CPerson. El problema a resolver es el
siguiente: “Se requiere lanzar un evento aquí que suministre el tiempo de
llegada de la persona”. Esto se resalta en color violeta en el código
anterior (ver procedimiento TimerSample_Timer).
Puesto que los objetos CPerson harán parte de un
objeto Collection en el cliente, no podemos
simplemente escribir en Declaraciones: Public
Event TimeArrive(DateTime As Date), y
esperar que nuestro codigo funcione. La clase CEventsHandler
resuelve esto, después de implementada la estrategia, el código luce así:
'//==============================================================
'// NAME : CPerson
'// AUTHOR : Harvey Triana (harvey@geocoweb.net)
'// DESCRIPTION :
Ejemplo del articulo vb6EventsHndler
'// www.mvps.org/vexpert
'//==============================================================
Option Explicit
Private m_FirstName As String
Private m_LastName As String
Private WithEvents TimerSample
As Timer
Private f As frmTimer
'//EH Implementación
Private m_EventsHandler As
CEventsHandler
'//EH Implementación
Public Property Set EventsHandler(v
As CEventsHandler)
Set m_EventsHandler = v
End Property
Public Property Let FirstName(v As String)
m_FirstName
= v
End Property
Public Property Get FirstName() As String
FirstName
= m_FirstName
End Property
Public Property Let LastName(v As String)
m_LastName
= v
End Property
Public Property Get LastName() As String
LastName
= m_LastName
End Property
Private Sub Class_Initialize()
Set f = New frmTimer
Load f
Set TimerSample
= f.tmrControl
End Sub
Private Sub Class_Terminate()
Unload f
'//EH Implementación
Set
m_EventsHandler = Nothing
End Sub
Public Sub StartSample()
Randomize Timer
TimerSample.Interval
= Rnd * 5000
TimerSample.Enabled
= True
End Sub
Private Sub TimerSample_Timer()
TimerSample.Enabled
= False
'//EH Implementación
m_EventsHandler.Trigger Me, "ArriveTime",
Time
End Sub
'//==============================================================
Las líneas de código que se adicionaron aparecen en
color violeta, y precedidas por el comentario EH Implementación. Ahora
CPerson tiene un nuevo miembro: EventsHandler,
el cual es una a propiedad de objeto explicita. La clase tampoco crea una
instancia de este objeto, el cual será pasado desde el cliente. Veamos un
cliente de muestra, un formulario que crea una colección de objetos CPerson, y que muestra la técnica de CEventsHandler.
'//==============================================================
'// NAME : CForm1
'// AUTHOR : Harvey Triana (harvey@geocoweb.net)
'// DESCRIPTION :
Ejemplo del articulo vb6EventsHandler
'// www.mvps.org/vexpert
'//==============================================================
Option Explicit
Private colPersons As Collection
'//EH Implementación
Private WithEvents EventsHandler As CEventsHandler
'//EH Implementación
Private Sub EventsHandler_RaisedEvent(
_
Sender As
Object, _
EventName As String, _
EventArgs As Variant)
Select
Case EventName
Case
"ArriveTime"
Print "Llego " & Sender.FirstName & " " & _
Sender.LastName & " a las " & EventArgs
End Select
End Sub
Private Sub Form_Load()
'//EH Implementación
Set EventsHandler = New CEventsHandler
Call LoadPersonsCollection
End Sub
Private Sub LoadPersonsCollection()
Dim
o As CPerson
'//ejemplo
Set colPersons =
New Collection
Set o = New CPerson
o.FirstName
= "Jonh"
o.LastName
= "Lennon"
colPersons.Add
o
Set o = Nothing
Set o = New CPerson
o.FirstName
= "Elenor"
o.LastName
= "Rigby"
colPersons.Add
o
Set o = Nothing
Set o = New CPerson
o.FirstName
= "Paul"
o.LastName
= "McCarney"
colPersons.Add
o
Set o = Nothing
'//mostrar el formulario cliente
Show
'//EH Implementación
For Each o In colPersons
Set o.EventsHandler = EventsHandler
o.StartSample
Next
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set colPersons
= Nothing
'//EH Implementación
Set EventsHandler = Nothing
End Sub
'//==============================================================
Este formulario crea tres personas desde Form_Load, luego pasa una referencia de un objeto EventsHandler a cada objeto CPerson,
y a su vez inicia un proceso Timer. Al ejecutar el
proyecto, se visualiza que cada objeto CPerson
lanza el evento de manera independiente, y que se puede saber “quien” lanza
el evento a través del objeto Sender de los
argumentos del evento RaisedEvent (evento
lanzado) de la clase CEventsHandler.
Este formulario crea tres personas desde Form_Load, luego pasa una referencia de un objeto EventsHandler a cada objeto CPerson,
y a su vez inicia un proceso Timer. Al ejecutar el
proyecto, se visualiza que cada objeto CPerson
lanza el evento de manera independiente, y que se puede saber “quien” lanza
el evento a través del objeto Sender de los
argumentos del evento RaisedEvent (evento lanzado)
de la clase CEventsHandler.
Conclusión
Observe algunos detalles del ejemplo anterior. Primero, la clase CPerson
no tiene declaración de eventos. Segundo, el evento RaisedEvent
se puede emplear para manejar un sin numero de eventos, basta un Select Case al nombre del evento para
diferenciarlos. Tercero, el argumento EventArgs por
ser un Variant puede contener un dato, o muchos (un
array o matriz),
es meramente circustancial, ya que al tener
el objeto Sender, este contiene toda la información
del objeto que envió el evento.
El este articulo mostre como
se pueden implementar fácilmente colecciones de objetos que generen eventos.
En la segunda parte del ariculo, diré como se
pueden lanzar eventos desde un formulario u objeto que cargado desde la
interfaz Form u Object.
|