miércoles, 10 de marzo de 2010

Tutorial sobre los módulos de clase VB .Net

1 - Introducción
Bien, suponga que ha creado una simulación de dinosaurios con las clases Estegosaurio, Triceratops y Tiranosaurio. Como toque final, quiere que el Tiranosaurio ruja y que, cuando lo haga, los demás dinosaurios se enteren.
Si la clase Tiranosaurio tiene un evento Rugir, puede controlar dicho evento en todas las demás clases de dinosaurios. En este tema se describe la declaración y el control de eventos en los módulos de clase.
Nota Niños, no intentéis esto en casa, al menos no con muchos dinosaurios. La conexión de cada uno de los dinosaurios con todos los demás dinosaurios mediante eventos podría hacer tan lentos a los dinosaurios que los objetos mamíferos ganarían la simulación.

Las propiedades y los métodos se dice que pertenecen a las interfaces entrantes porque se llaman desde fuera del objeto. Por otra parte, los eventos se dice que forman parte de las interfaces salientes, puesto que se inician desde dentro del objeto y se controlan fuera del mismo.
En los temas siguientes se describe el proceso de declarar, desencadenar y controlar eventos, con ejemplos.
Declarar y desencadenar eventos Como los procedimientos, los eventos (y sus argumentos) se tienen que declarar. Para que tenga lugar un evento declarado, el objeto tiene que desencadenarlo.
Controlar los eventos de un objeto Los eventos que desencadenan los objetos pueden controlarse mediante otros objetos si utiliza variables declaradas con la palabra clave WithEvents.
Comparar WithEvents con los eventos de los controles de los formularios Similitudes y diferencias entre los procedimientos de evento asociados con las variables WithEvents y los asociados con los controles de los formularios.
Agregar un evento a un formulario Un breve ejemplo paso a paso de cómo agregar un evento personalizado a un formulario.
Resumen de cómo declarar, desencadenar y controlar eventos Un resumen del proceso del uso de eventos en las clases.
Para obtener más información En Creación de componentes ActiveX, de la Guía de herramientas componentes proporcionada con la Edición profesional y la Edición empresarial, se describe el uso de los eventos para el diseño de sus propios componentes software.
Para obtener una descripción de la mejor manera de administrar los dinosaurios, consulte “Polimorfismo”, más adelante en este tema.


2 - Declaración y desencadenamiento de eventos

Suponga por un momento que tiene una clase Widget. Su clase Widget tiene un método que puede tardar bastante tiempo en ejecutarse y le gustaría que su aplicación pudiera ofrecer algún tipo de indicación de su estado.
Desde luego, puede hacer que el objeto Widget muestre un cuadro de diálogo de porcentaje de terminación, pero de esta forma siempre tendría este cuadro de diálogo en todos los proyectos en los que utilice la clase Widget. Una buena práctica de diseño de objetos es permitir que la aplicación que utiliza un objeto controle la interfaz de usuario, a menos que el único propósito del objeto sea administrar un formulario o un cuadro de diálogo.
El propósito de Widget es realizar otras tareas, por lo que es razonable incluir un evento PercentDone y permitir que el procedimiento que llama a los métodos de Widget controlen dicho evento. El evento PercentDone también puede ofrecer un mecanismo de cancelación de la tarea.
Puede empezar a crear el ejemplo de código de este tema si abre un proyecto Exe estándar y agrega dos botones y una etiqueta a Form1. En el menú Proyecto, seleccione Agregar módulo de clase para agregar un módulo de clase al proyecto. Asigne un nombre a los objetos como se muestra en la tabla siguiente.
Objeto Propiedad Valor
Módulo de clase Name Widget
Primer botón Caption Iniciar tarea
Segundo botón Caption Cancelar
Etiqueta Name lblPercentDone
Caption “0”

La clase Widget
Los eventos se declaran en la sección Declaraciones de los módulos de clase, mediante la palabra clave Event. Un evento puede tener argumentos ByVal y ByRef, como se muestra en el evento PercentDone de Widget:
Option Explicit
Public Event PercentDone(ByVal Percent As Single,ByRef Cancel As Boolean)

Cuando el objeto que llama recibe un evento PercentDone, el argumento Percent contiene el porcentaje realizado de la tarea . El argumento ByRef Cancel puede establecerse a True para cancelar el método que desencadenó el evento.
Nota Puede declarar argumentos de evento como si fueran argumentos de procedimientos, con las excepciones siguientes: los eventos no pueden tener argumentos con nombre, argumentos opcionales ni argumentos ParamArray. Los eventos no tienen valores de retorno.
Desencadenamiento del evento PercentDone
El evento PercentDone lo desencadena el método LongTask de la clase Widget. El método LongTask recibe dos argumentos: el periodo de tiempo durante el que el método realizará el trabajo y el mínimo intervalo de tiempo antes de cual LongTask se detenga para desencadenar el evento PercentDone.
El evento PercentDone lo desencadena el método LongTask de la clase Widget. El método LongTask recibe dos argumentos: el periodo de tiempo durante el que el método realizará el trabajo y el mínimo intervalo de tiempo antes de cual LongTask se detenga para desencadenar el evento PercentDone
Public Sub LongTask(ByVal Duration As Single, ByVal MinimumInterval As Single)
 Dim sngThreshold As Single
 Dim sngStart As Single
 Dim blnCancel As Boolean
 
 ' La función Timer devuelve el número fraccionario
 ' de segundos desde medianoche, como Single.
 sngStart = Timer
 sngThreshold = MinimumInterval
 
 Do While Timer < (sngStart + Duration)
  ' En una aplicación real, una parte del trabajo 
  ' se haría aquí en cada paso del bucle.
  
  If Timer > (sngStart + sngThreshold) Then
   RaiseEvent PercentDone(sngThreshold / Duration, blnCancel)
   ' Comprueba si la operación se canceló.
   If blnCancel Then Exit Sub
   sngThreshold = sngThreshold + MinimumInterval
  End If
 Loop
End Sub
          
Cada MinimumInterval segundos se desencadena el evento PercentDone. Cuando el evento termina, LongTask comprueba si el argumento Cancel es True
Nota Por simplificar, LongTask asume que se conoce con antelación la duración de la tarea. Pero casi nunca se puede saber. La división de una tarea en partes iguales puede ser difícil y normalmente lo que importa a los usuarios es únicamente el tiempo que pasa hasta que reciben una indicación de que se está realizando alguna tarea.
Para obtener más información Ahora que ha declarado un evento y lo ha desencadenado, ¿cómo puede hacer que lo controle otro objeto? “Control de los eventos de un objeto” continúa con la historia del objeto Widget.


3 - Control de los eventos de un objeto

Un objeto que desencadena eventos se conoce como origen del evento. Para controlar los eventos desencadenados por un origen de eventos, puede declarar una variable de la clase del objeto con la palabra clave WithEvents.
En este tema se continúa el ejemplo del objeto Widget empezado en “Declaración y desencadenamiento eventos”. Para controlar el evento PercentDone de un objeto Widget, coloque el código siguiente en la sección Declaraciones de Form1:
Option Explicit
Private WithEvents mWidget As Widget
Private mblnCancel As Boolean
La palabra clave WithEvents especifica que la variable mWidget se utilizará para controlar los eventos de un objeto. El tipo de objeto se especifica con el nombre de la clase a partir de la cual se creó el objeto.
La variable mWidget se declara en la sección Declaraciones de Form1, puesto que las variables WithEvents tienen que ser variables de nivel de módulo. Esto es cierto independientemente del tipo de módulo en que las coloque.
La variable mblnCancel se utilizará para cancelar el método LongTask.

Limitaciones de las variables WithEvents
Debe tener en cuenta las siguientes limitaciones del uso de las variables WithEvents:
  • Una variable WithEvents no puede ser una variable genérica de objeto. Es
    decir, no puede declararla As Object: tiene que especificar el nombre de la
    clase cuando declare la variable.
  • No puede declarar una variable WithEvents como As New. El objeto origen
    del evento se debe crear y asignar a la variable WithEvents de forma
    explícita.
  • No puede declarar variables WithEvents en un módulo estándar. Sólo puede
    declararlas en módulos de clase, en módulos de formulario y en otros módulos
    que definan clases.
  • No puede crear matrices de variables WithEvents.

Escritura de código para controlar un evento
En cuanto declara una variable WithEvents, el nombre de la variable aparece en la lista desplegable de la izquierda de la ventana de código del módulo. Cuando seleccione mWidget, los eventos de la clase Widget aparecerán en la lista desplegable de la derecha, como se muestra en la figura 9.9.

La selección de un evento presentará el procedimiento de evento correspondiente, con el prefijo mWidget_. Todos los procedimientos de evento asociados con una variable WithEvents tendrán el nombre de la variable como prefijo. Agregue el código siguiente al procedimiento de evento mWidget_PercentDone.
Private Sub mWidget_PercentDone(ByVal Percent As Single, Cancel As Boolean)
 lblPercentDone.Caption = CInt(100 * Percent) & "%"
 DoEvents
 If mblnCancel Then Cancel = True
End Sub

Cada vez que se desencadena un evento PercentDone, el procedimiento de evento presenta el porcentaje de terminación en un control Label. La instrucción DoEvents permite que la etiqueta se vuelva a dibujar y también ofrece al usuario la oportunidad de hacer clic en el botón Cancelar. Agregue el código siguiente al evento Click del botón cuyo título es Cancelar.
Private Sub Command2_Click()
 mblnCancel = True
End Sub 

Si el usuario hace clic en el botón Cancelar mientras se ejecuta LongTask, el evento Command2_Click se ejecutará tan pronto como la instrucción DoEvents permita procesar el evento. La variable de nivel de módulo mblnCancel se establece a True y el evento mWidget_PercentDone la comprueba y establece el argumento ByRef Cancel a True.

Conexión de una variable WithEvents con un objeto
Form1 está preparado para controlar los eventos del objeto Widget. Todo lo que queda es encontrar un Widget en alguna parte.
Cuando declara una variable WithEvents en tiempo de diseño, no hay ningún objeto asociado con ella. Una variable WithEvents es como cualquier otra variable de objeto. Tiene que crear un objeto y asignar a la variable WithEvents una referencia al objeto. Agregue el código siguiente al procedimiento de evento Form_Load para crear el objeto Widget.
Private Sub Form_Load()
 Set mWidget = New Widget
End Sub 

Cuando se ejecuta el código anterior, Visual Basic crea un objeto Widget y conecta sus eventos con los procedimientos de evento asociados con mWidget. A partir de este punto, siempre que Widget desencadene el evento PercentDone se ejecutará el procedimiento de evento mWidget_PercentDone.
Para llamar al método LongTask, agregue el código siguiente al evento Click del botón cuyo título es Iniciar tarea.
Cuando se ejecuta el código anterior, Visual Basic crea un objeto Widget y conecta sus eventos con los procedimientos de evento asociados con mWidget. A partir de este punto, siempre que Widget desencadene el evento PercentDone se ejecutará el procedimiento de evento mWidget_PercentDone.
Para llamar al método LongTask, agregue el código siguiente al evento Click del botón cuyo título es Iniciar tarea.
' Botón Iniciar tarea.
Private Sub Command1_Click()
 mblnCancel = False
 lblPercentDone.Caption = "0%"
 lblPercentDone.Refresh
 
 Call mWidget.LongTask(14.4, 0.66)
 
 If Not mblnCancel Then lblPercentDone.Caption = 100
End Sub

Antes de llamar al método LongTask tiene que inicializar la etiqueta que presenta el porcentaje de terminación y tiene que establecer a False el indicador booleano de nivel de módulo de la cancelación del método.
LongTask se invoca con una duración de la tarea de 14,4 segundos. El evento PercentDone se desencadenará cada dos tercios de segundo. Cada vez que se desencadena el evento se ejecuta el procedimiento de evento mWidget_PercentDone.
Cuando termina LongTask se comprueba mblnCancel para ver si LongTask ha terminado normalmente o si terminó porque mblnCancel se estableció a True. El porcentaje de terminación sólo se actualiza en el primer caso.

Ejecución del programa
Presione F5 para poner el proyecto en modo de ejecución. Haga clic en el botón Iniciar tarea. Cada vez que se desencadena el evento PercentDone, la etiqueta se actualiza con el porcentaje terminado de la tarea. Haga clic en el botón Cancelar para detener la tarea. Observe que la apariencia del botón Cancelar no cambia de forma inmediata cuando hace clic en él. El evento Click no puede producirse mientras la instrucción DoEvents no permita el proceso de eventos.
Puede que encuentre instructivo ejecutar el programa con F8 y recorrer el código de línea en línea. Puede ver con claridad cómo la ejecución entra en LongTask y después vuelve a pasar brevemente por Form1 cada vez que se desencadena el evento PercentDone.
¿Qué ocurriría si mientras la ejecución vuelve al código de Form1 se llama otra vez al método LongTask? Confusión, caos y, tarde o temprano (si ocurre todas las veces que se desencadena el evento), un desbordamiento de la pila.
Control de eventos de un Widget diferente
Puede hacer que la variable mWidget controle los eventos de un objeto Widget diferente si asigna a mWidget una referencia al nuevo objeto Widget. De hecho, puede hacer que el código de Command1 realice esto cada vez que hace clic en el botón si agrega dos líneas de código:
Set mWidget = New Widget '<- Línea nueva.
Call mWidget.LongTask(14.4, 0.66)
Set mWidget = Nothing '<- Línea nueva.

El código anterior crea un nuevo objeto Widget cada vez que se hace clic en el botón. Tan pronto como termine el método LongTask se libera la referencia al objeto Widget mediante el establecimiento de mWidget a Nothing y se destruye el antiguo objeto Widget.
Una variable WithEvents sólo puede contener una referencia de objeto al mismo tiempo, de modo que si asigna a mWidget un objeto Widget diferente, no podrá controlar los eventos del objeto Widget anterior. Si mWidget es la única variable de objeto que contiene una referencia al Widget antiguo, el objeto se destruirá.
Nota Puede declarar tantas variables WithEvents como necesite, pero no se aceptan matrices de variables WithEvents.

Terminación del control de eventos para una variable WithEvents
Mientras haya un objeto Widget asignado a la variable mWidget, los procedimientos de evento asociados con mWidget se invocarán cada vez que el objeto Widget desencadene un evento. Para terminar el control de eventos, puede establecer mWidget a Nothing, como se muestra en el siguiente fragmento de código.
' Termina el control de eventos para mWidget.
Set mWidget = Nothing
Cuando una variable WithEvents se establece a Nothing, Visual Basic desconecta los eventos del objeto desde los procedimientos de evento asociados con la variable.
Importante Una variable WithEvents contiene una referencia de objeto, como cualquier otra variable de objeto. Esta referencia de objeto ayuda a mantener vivo un objeto. Cuando establezca todas las referencias de un objeto a Nothing para destruirlo, no olvide las variables declaradas WithEvents.

Para obtener más información Los procedimientos de evento asociados con las variables WithEvents se parecen mucho a los procedimientos de evento de los controles de los formularios. En “Comparación de WithEvents con los eventos de los controles de los formularios” se describen las similitudes y las diferencias.


4 - Comparación de WithEvents con los eventos de los controles de los formularios

Probablemente habrá observado algunas similitudes entre la manera de utilizar las variables WithEvents y la manera de controlar los eventos desencadenados por los controles de un formulario. En ambos casos, cuando selecciona el evento en la lista desplegable de la derecha de una ventana de código obtiene un procedimiento de evento que contiene los argumentos apropiados del evento
De hecho, el mecanismo es exactamente el mismo. Un control se trata como una propiedad de la clase de formulario y el nombre de dicha propiedad es el valor asignado a la propiedad Name del control en la ventana Propiedades.
Es como si hubiera una variable Public de nivel de módulo con el mismo nombre que el control y todos los nombres de los procedimientos de evento del control empezarán por dicho nombre de variable, como si fueran variables WithEvents.
Puede comprobar fácilmente esto si declara la variable mWidget como Public en lugar de Private.Cuando hace esto, mWidget se muestra en el Examinador de objetos como una propiedad de Form1, igual que los controles del formulario.
La diferencia entre los dos casos es que Visual Basic crea automáticamente instancias de todos los controles de un formulario cuando éste se crea, mientras usted tiene que crear sus propias instancias de las clases cuyos eventos quiere controlar y asignar a las variables WithEvents referencias a dichos objetos.
Para obtener más información Puede agregar sus propios eventos a los formularios, como se describe en “Agregar un evento a un formulario”.


5 - Agregar un evento a un formulario


El siguiente procedimiento paso a paso muestra cómo puede crear eventos personalizados para los formularios. Para ejecutar este ejemplo, abra un nuevo proyecto Exe estándar y haga lo siguiente:

Para agregar un evento a Form1
1 En el menú Proyecto, seleccione Agregar módulo de clase para agregar un módulo de clase al proyecto. Coloque el código siguiente en la sección Declaraciones de Class1:
Public Property Get Form1() As Form1
Set Form1 = mForm1
End Property

Public Property Set Form1(ByVal NewForm1 As Form1)
Set mForm1 = NewForm1
End Property
Si utiliza Ver procedimiento los procedimientos de propiedad no se pueden ver al mismo tiempo. Haga clic en el botón Ver módulo completo de la esquina inferior izquierda de la ventana código para pasar a Ver módulo completo. Puede volver a Ver procedimiento si hace clic en el botón Ver procedimiento que está al lado. (Mueva el mouse (ratón) sobre los botones para identificarlos.)
2 Agregue el código siguiente a la sección Declaraciones de Form1:
Evento Gong
Private mc1 As Class1

Ahora que ha creado Class1, puede crear una variable del tipo Class1. Este procedimiento cambia varias veces entre Form1 y Class1, puesto que un paso en un módulo requiere agregar antes código en el otro.

3 Vuelva a Class1 y agregue el código siguiente a la sección Declaraciones.
Private WithEvents mForm1 As Form1

Como se describe en “Agregar eventos a las clases”, la palabra clave WithEvents significa que esta instancia de Form1 está asociada con eventos. Observe que este paso no era posible hasta la creación del evento Gong.
4 En la lista desplegable de la izquierda (Objeto) de la ventana Código de Class1, seleccione mForm1 para obtener el procedimiento de evento Gong. Agregue el código siguiente al procedimiento de evento:
Private Sub mForm1_Gong()
MsgBox "¡Gong!"
End Sub

5 Vuelva a Form1. En la lista desplegable Objeto, seleccione Form. En la lista desplegable de la derecha (Procedimiento), seleccione Load. Agregue el código siguiente al procedimiento de evento:
Private Sub Form_Load()
Set mc1 = New Class1
Set mc1.Form1 = Me
End Sub

La primera línea crea un objeto Class1 y la segunda asigna a la propiedad Form1 (creada en el paso 1) una referencia a Form1 (es decir, Me: cuando se encuentra en la ventana Código de Form1, Me hace referencia a Form1; cuando se encuentra en la ventana Código de Class1, Me hace referencia a Class1).

6 Coloque tres cuadros de texto en Form1. Utilice las listas desplegables Objeto y Procedimiento para seleccionar el procedimiento de evento Change de cada control y coloque esta línea de código en cada uno de ellos:
Private Sub Text1_Change()
RaiseEvent Gong
End Sub

Cada vez que cambia el contenido de un cuadro de texto, se desencadena el evento Gong del formulario.

7 Presione F5 para ejecutar el proyecto. Cada vez que escriba un carácter en uno de los cuadros de texto, el cuadro de mensajes emitirá un pitido. Es un poco molesto, pero le muestra cómo puede agregar un evento a un formulario y, de esta forma, obtener notificaciones desde varios controles.
Como se ha mostrado en “Declaración y desencadenamiento de eventos”, puede agregar argumentos a los eventos. Por ejemplo, puede pasar el nombre del control o, mejor aún, una referencia al control, al receptor del evento.


6 - Resumen de cómo declarar, desencadenar y controlar eventos

Para agregar un evento a una clase y utilizar después el evento:
  • En la sección Declaraciones del módulo de clase que define la clase,
    utilice la instrucción Event para declarar el evento con los argumentos que
    desee que tenga. Los eventos son siempre Public.

    Nota Los eventos no pueden tener argumentos con nombre, argumentos opcionales ni argumentos
    ParamArray. Los eventos no tienen valores de retorno.
  • En los lugares apropiados del código del módulo de clase, utilice la
    instrucción RaiseEvent para desencadenar el evento, junto con los argumentos
    necesarios.
  • En la sección Declaraciones del módulo que va a controlar el evento,
    agregue una variable del tipo de clase, mediante la palabra clave WithEvents.
    Tiene que ser una variable a nivel de módulo.
  • En la lista desplegable de la izquierda de la ventana Código, seleccione
    la variable declarada WithEvents.
  • En la lista desplegable de la derecha, seleccione el evento que quiere
    controlar. (Puede declarar múltiples eventos para una clase.)
  • Agregue código al procedimiento de evento mediante los argumentos
    suministrados.

1 comentario:

  1. Aprendiendo VB.NET 2010, de casualidad no tienen un ejemplo de cargar y leer un xml, en una variable almacenar un nuevo valor(como un nodo Addenda ) y buscar en el XMl cargado una palabra en especial y reemplazarla con el valor de una variable y sumarle la palabra encontrada?
    Saludos..

    ResponderEliminar