Sección de tutoriales y manuales vb

Tutorial sobre los módulos de clase

1 - Creación de sus propias clases

Volver al índice



 

 

Tutorial realizado por JOEL ALEJANDRO

1 - Creación de sus propias clases - Contenido

  1. Introducción
  2. Combinar tipos definidos por el usuario y procedimientos
  3. Personalizar las clases Form
  4. Módulos de clase paso a paso
  5. Depurar los módulos de clase
  6. Ciclo de vida de los formularios de Visual Basic
  7. Módulos de clase frente a módulos estándar

 

 


1 - Introducción

 

Si es un programador experimentado ya dispondrá de una biblioteca de funciones escritas a lo largo de los años. Los objetos no sustituyen a las funciones (seguirá escribiendo y utilizando funciones), sino que proporcionan una forma lógica de organizar procedimientos y datos.

En particular, las clases a partir de las cuales crea objetos combinan datos y procedimientos en una misma unidad. En los temas siguientes se explica cómo aumenta esto la coherencia del código y cómo le puede conducir a nuevas maneras de programar.

· Clases: combinar tipos definidos por el usuario y procedimientos Las clases son tipos definidos por el usuario con una determinada actitud. El encapsulamiento coloca el código y los datos dentro del mismo módulo para producir objetos capaces de proteger y validar sus propios datos.

· Personalizar las clases Form Ha estado creando clases durante años: cada vez que ha diseñado un formulario. Esto tiene consecuencias interesantes para el diseño de formularios.

· Módulos de clase paso a paso Breve introducción a los módulos de clase, que incluye la creación de la clase, la creación de objetos a partir de las clases y las reglas de la duración de los objetos.

· Depurar los módulos de clase Describe la opción de interceptación de errores Interrupción en módulo de clases, además de las teclas ALT+F8 y ALT+F5 para ir paso a paso por los errores o saltarlos.

· Ciclo de vida de los formularios de Visual Basic Los ciclos de vida de los formularios y de los controles que contienen siguen unas reglas ligeramente diferentes de las que se aplican a otros objetos.

· Módulos de clase frente a módulos estándar Hay diferencias importantes entre los módulos de clase y los módulos estándar. Su descripción le ayudará a escribir un código mejor para sus objetos.

 


2 - Combinar tipos definidos por el usuario y procedimientos

 

Los tipos definidos por el usuario son una poderosa herramienta para agrupar elementos relacionados de datos. Considere por ejemplo el tipo definido por el usuario llamado udtAccount definido a continuación:

Public Type udtAccount
	Number As Long
	Type As Byte
	CustomerName As String
	Balance As Double
End Type 

 

Puede declarar una variable del tipo udtAccount, establecer los valores de sus campos individualmente y después pasar todo el registro a procedimientos que lo impriman, lo guarden en una base de datos, realicen cálculos con él, validen sus campos, etc.

Al ser tan poderosos, los tipos definidos por el usuario presentan algunos problemas al programador. Puede crear un procedimiento Withdrawal que produce un error si el cobro sobrepasa el saldo de la cuenta, pero no puede hacer nada para evitar que el campo Balance se modifique desde otra parte del código del programa.

Es decir, la conexión entre los procedimientos y los tipos definidos por el usuario depende de la disciplina, la memoria y el conocimiento del programador que mantiene el código.

 

Objetos: tipos definidos por el usuario con una determinada actitud

La programación orientada a objetos resuelve este problema mediante la combinación de datos y procedimientos en una única entidad, como se muestra en la figura 9.5.

Figura 9.5 Los objetos combinan datos y procedimientos

 

Cuando el tipo definido por el usuario udtAccount se convierte en la clase Account, sus datos pasan a ser privados y los procedimientos que tienen acceso a ellos pasan a formar parte de la clase como propiedades y métodos. Esto es lo que significa el término encapsulamiento; es decir, un objeto es una unidad (una cápsula, si quiere) que contiene código y datos.

Cuando crea un objeto Account a partir de la clase, la única manera de tener acceso a sus datos es mediante las propiedades y los métodos que forman su interfaz. En el siguiente fragmento de código se muestra cómo implementan el encapsulamiento los procedimientos de la clase Account:

' El saldo de la cuenta no se ve fuera del código.
Private mdblBalance As Double

' La propiedad de sólo lectura Balance permite que el
' código externo lea el saldo de la cuenta.
Public Property Get Balance() As Double
    Balance = mdblBalance
End Property

' El método Withdrawal cambia el saldo de la cuenta,
' pero sólo si no queda en números rojos.
Public Sub Withdrawal(ByVal Amount As Double)
    If Amount > Balance Then
        Err.Raise Number:=vbObjectError + 2081, _
        Description:="Números rojos"
    End If
    mdblBalance = mdblBalance - Amount
End Sub 

 

Por el momento, no se preocupe de cómo se incluyen los procedimientos en la clase ni de comprender la sintaxis de los procedimientos de propiedad y de las variables privadas. Lo importante es recordar que puede definir un objeto que encapsula y valida sus propios datos.

Con el objeto Account nunca tiene que preocuparse de si ha utilizado los procedimientos correctos para actualizar la cuenta, porque los únicos procedimientos que puede utilizar están incorporados al objeto.

Para obtener más información “Personalización de las clases Form” ubica la creación de propiedades y métodos en un marco con el que ya está familiarizado. Más adelante, “Agregar propiedades y métodos a una clase” le explicará la sintaxis.

Puede ver acerca de los tipos definidos por el usuario en “Crear sus propios tipos de datos”, en “Más acerca de la programación”.

Para obtener detalles acerca de los procedimientos Sub y Function, vea “Introducción a los procedimientos” en “Fundamentos de programación”.

 


3 - Personalización de las Clases Form

 

Puede que le sorprenda descubrir que ya ha estado creando clases durante todo el tiempo que ha estado programando en Visual Basic. Es cierto: Form1, ese nombre tan familiar que aparece en cada proyecto que ha empezado, es realmente una clase.
Para comprobarlo, abra un nuevo proyecto Exe estándar. Agregue un botón a Form1 y coloque el siguiente código en el evento Click:

Private Sub Command1.Click()
	Dim f As New Form1
	f.Show
End Sub
          

 

Presione F5 para ejecutar el proyecto y haga clic en el botón. No se sorprenda; hay otra instancia de Form1. Haga clic en su botón: hay otra. Todas las instancias que crea parecen iguales y tienen el mismo comportamiento, puesto que todas son instancias de la clase Form1.

¿Qué está pasando?

Si ha visto “Trabajar con objetos”, en “Fundamentos de programación”, sabrá que una variable de objeto declarada As New contiene Nothing hasta la primera vez que hace referencia a ella desde el código. La primera vez que utiliza la variable, Visual Basic observa que contiene el valor especial Nothing y crea una instancia de la clase. (Y hace bien, porque si no, f.Show provocaría un error.)

Yo y mi variable global oculta

Puede que se pregunte cómo puede hacer referencia a Form1 en el código, como si fuera una variable de objeto. No hay magia por ninguna parte. Visual Basic crea una variable oculta de objeto global por cada clase de formulario. Es como si Visual Basic hubiera agregado la siguiente declaración al proyecto:

Public Form1 As New Form1

 

Cuando selecciona Form1 como objeto inicial o cuando escribe Form1.Show en el código estáhaciendo referencia a esta variable oculta de objeto global. Como está declarada As New, se crea una instancia de la clase Form1 la primera vez que utiliza esta variable declarada previamente en el código.

La razón por la que esta declaración está oculta es porque Visual Basic la modifica cada vez que se modifica la propiedad Name de un formulario. De esta forma, la variable oculta siempre tiene el mismo nombre que la clase formulario.

Una pregunta

¿Cuáles de las instancias de Form1 creadas en el ejercicio anterior estaban asociadas con la variable global oculta? Si contesta que la primera, tiene razón. Form1 es el objeto inicial predeterminado del proyecto y para Visual Basic eso es como si utilizara la variable global Form1 declarada previamente en el código.

Sugerencia Después de descargar un formulario, siempre debe establecer todas las referencias del formulario a Nothing para liberar la memoria y los recursos utilizados por el formulario. La referencia que más se suele pasar por alto es la variable oculta de formulario global.

¿Qué ocurre con las demás instancias de Form1?

En “Fundamentos de programación” ha visto que para hacer referencia a un objeto necesita una variable de objeto y que un objeto sólo existe mientras haya al menos una variable de objeto que contenga una referencia al mismo. Entonces, ¿por qué se mantienen todas las demás instancias?

La segunda instancia de Form1, y todas las demás, tenían una variable de objeto mientras se utilizaban sus métodos Show. Después, esa variable perdió su alcance y se estableció a Nothing. Pero Visual Basic mantiene una colección especial llamada Forms sobre la que puede profundizar en “Más acerca de los formularios”, en “Crear una interfaz de usuario”. La colección Forms contiene una referencia a cada uno de los formularios cargados en el proyecto, de modo que siempre puede encontrarlos y controlarlos.

Nota Como irá observando, esto no es cierto para todas las clases. Por ejemplo, las clases que diseña no tendrán variables globales ocultas ni colecciones globales para administrarlas: éstas son características especiales de las clases de formulario. Sin embargo, puede declarar sus propias variables globales y sus propias colecciones como se describe en “Creación de sus propias clases de colección”.

Propiedades, métodos y eventos de las clases Form

La primera vez que agregó una propiedad a una clase de formulario probablemente lo hizo de forma visual, mediante la colocación de un botón de comando (o algún otro control) en Form1. Al hacerlo, agregó una propiedad Command1 de sólo lectura a la clase de formulario. A continuación invocó esta propiedad de Form1 cuando tenía que invocar un método o una propiedad del botón de comando:

Command1.Caption = "Haga clic aquí"

 

Cuando modificaba la propiedad Name de cualquier control de un formulario, Visual Basic modificaba ocultamente el nombre de la propiedad de sólo lectura para que siempre coincidieran.
Si todavía tiene abierto el proyecto del ejercicio anterior, puede ver esta propiedad Command1 presionando F2 para abrir el Examinador de objetos. En el cuadro Proyecto/Biblioteca, seleccione Project1. Verá Form1 en el panel Clases. En el panel Miembros, desplácese hasta encontrar Command1 y selecciónelo.

Command1 tiene un símbolo de propiedad al lado y si observa el panel descripción, verá que es una propiedad WithEvents. Como verá en “Agregar eventos a las clases”, esto quiere decir que la propiedad (o la variable de objeto) tiene un procedimiento de evento asociado con ella. Puede que uno de dichos procedimientos de evento, Command1_Click (), contenga el primer código de Visual Basic escrito por usted.

Espere, aún hay más

Colocar controles en un formulario no es la única manera de agregar nuevos miembros a la clase de formulario. Puede agregar sus propias propiedades, métodos y eventos personalizados con la misma facilidad con la que crea nuevas variables y procedimientos.
Para comprobarlo, agregue el código siguiente a la sección Declaraciones de Form1:

' Propiedad Comment de la clase Form1.
Public Comment As String
	
Agregue el código siguiente al evento Click de Form1:
Private Sub Form_Click()
	MsgBox Comment, , "Mi comentario es:"
End Sub
          

 

Por último, modifique el código del procedimiento de evento Command1_Click () agregando un línea, de la siguiente manera:

Private Sub Command1.Click()
	Dim f As New Form1
	f.Comment = InputBox("¿Cuál es mi comentario?")
	f.Show
End Sub 

 

Presione F5 para ejecutar el proyecto. Haga clic en Command1 y, cuando aparezca el cuadro de entrada, escriba cualquier cosa y haga clic en Aceptar. Cuando aparezca la nueva instancia de Form1, haga clic en ella para reproducir su propiedad Comment.

Haga clic en la primera instancia de Form1 y observe que la propiedad Comment está vacía. Puesto que Visual Basic creó esta instancia como objeto inicial, nunca tuvo oportunidad de establecer su propiedad Comment.

Los formularios pueden invocar los métodos de otros formularios

Si se ha fijado bien, puede que haya advertido que el código agregado a la clase Form1 no establecía la propiedad Comment del objeto, sino que establecía la propiedad Comment de la nueva instancia de Form1 que creaba.

Esta capacidad de los formularios para establecer las propiedades y utilizar los métodos de otros formularios es una técnica muy útil. Por ejemplo, cuando un formulario MDI abre una nueva ventana secundaria, puede inicializar la nueva ventana mediante el establecimiento de sus propiedades y llamadas a sus métodos.

También puede utilizar esta técnica para pasar información de un formulario a otro.

Sugerencia Puede crear eventos personalizados en los formularios. “Agregar un evento a un formulario”, más adelante en este tema, le proporciona el procedimiento que debe seguir.

Otros tipos de módulos

Puede agregar propiedades, métodos y eventos a las clases de formulario mediante la escritura de código en sus módulos de código. De la misma forma, puede agregar propiedades, métodos y eventos a los módulos de clase y, si tiene la Edición profesional o la Edición empresarial de Visual Basic, a los módulos de código del control de usuario y del documento de usuario.

Cuando lea “Agregar propiedades y métodos a una clase” y “Agregar eventos a una clase”, recuerde que todo lo que lea se aplica a las clases de formulario así como a los módulos de clase.

Para obtener más información ¿Qué es un módulo de clase? En “Módulos de clase paso a paso” se muestra cómo puede definir una clase y se ilustra el ciclo de vida de los objetos creados a partir de dicha clase.


4 - Módulos de clase, paso a paso

En este ejemplo se muestra cómo puede utilizar los módulos de clase para definir clases a partir de las cuales puede crear después objetos. También le explica cómo puede crear propiedades y métodos en la nueva clase y le demuestra cómo se crean y se destruyen los objetos.

Abra un nuevo proyecto Exe estándar e inserte un módulo de clase con Agregar módulo de clase del menú Proyecto. Coloque cuatro botones de comando en el formulario. En la tabla siguiente se enumeran los valores de propiedades que tiene que establecer para los objetos de este ejemplo.

Objeto Propiedad Valor
Módulo de clase Name Thing
Command1 Caption Mostrar Thing
Command2 Caption Invertir el nombre de Thing
Command3 Caption Crear nuevo Thing
Command4 Caption Thing temporal

 

Nota Los módulos de clase se guardan en archivos con la extensión .cls.

Agregue lo siguiente a la sección Declaraciones del módulo de clase:

Option Explicit
Public Name As String
Private mdtmCreated As Date

 

La variable Name será una propiedad del objeto Thing, puesto que se declara Public.

Nota No confunda esta propiedad Name con la propiedad Name del módulo de clase, establecida según las indicaciones de la tabla anterior. (La propiedad Name del módulo de clase asigna el nombre a la clase Thing.) ¿Por qué se da a la clase Thing una propiedad Name? Sería mejor preguntase, ¿por qué no? Puede que desee asignar una propiedad Name a la clase Thing porque Things debe tener nombres. Recuerde que no hay restricciones especiales en cuanto a los nombres de las propiedades y los métodos utilizados en Visual Basic. Puede utilizar los mismos nombres de propiedades y métodos en las clases.

La variable mdtmCreated es un miembro privado de datos que se utiliza para almacenar el valor de la propiedad de sólo lectura Created. La propiedad Created devuelve la fecha y la hora en que se creó el objeto Thing. Para implementar la propiedad Created, agregue el siguiente procedimiento Property Get a la sección Declaraciones del módulo de clase:

Property Get Created() As Date
	Created = mdtmCreated
End Property 

 

Nota Si agrega el procedimiento de propiedad mediante el cuadro de diálogo Agregar procedimiento del menú Herramientas, asegúrese de eliminar la declaración Property Let que se agrega automáticamente con este cuadro de diálogo. Property Let sólo se requiere en las propiedades de lectura y escritura, como se explica en “Funcionamiento de los procedimientos de propiedad”.

El objeto Thing tiene un método, ReverseName, que simplemente invierte el orden de las letras de la propiedad Name. No devuelve ningún valor, por lo que está implementado como procedimiento Sub. Agregue el siguiente procedimiento Sub al módulo de clase.

Public Sub ReverseName()
	Dim intCt As Integer
	Dim strNew As String
	For intCt = 1 To Len(Name)
		strNew = Mid$(Name, intCt, 1) & strNew
	Next
	Name = strNew
End Sub 

 

Los módulos de clase tienen dos eventos, Initialize y Terminate. En la lista desplegable Objeto del módulo de clase, seleccione Class. La lista desplegable Procedimiento mostrará los eventos. Coloque el código siguiente en los procedimientos de evento:

Private Sub Class_Initialize()
	' Establece la fecha y hora de creación del objeto, 
	' que devolverá la propiedad Created.
	mdtmCreated = Now
	' Muestra las propiedades del objeto.
	MsgBox "Nombre: " & Name & vbCrLf & "Creado: " _
	& Created, , "Inicialización de Thing"
End Sub

Private Sub Class_Terminate()
	' Muestra las propiedades del objeto.
	MsgBox "Nombre: " & Name & vbCrLf & "Creado: " _
	& Created, , "Terminación de Thing"
End Sub 

 

Normalmente, el procedimiento de evento Initialize contiene el código que se ejecuta en el momento de la creación del objeto, por ejemplo la fecha y la hora para la propiedad Created. El evento Terminate contiene el código necesario para liberar el objeto cuando éste se destruye.

En este ejemplo, los dos eventos se utilizan principalmente para ofrecer una indicación visual de la creación y destrucción de un objeto Thing

 

Uso del objeto Thing

Agregue esta declaración a la sección Declaraciones del módulo de formulario:

Option Explicit
Private mth As Thing

 

La variable mth almacenará una referencia a un objeto Thing, que se creará en el evento Load del formulario. Coloque el código siguiente en el procedimiento de evento Form_Load y en los procedimientos de evento Click de los cuatro botones.

Private Sub Form_Load()
	Set mth = New Thing
	mth.Name = InputBox("Escriba un nombre para Thing")
End Sub

' Botón "Mostrar Thing"
Private Sub Command1_Click()
	MsgBox "Nombre: " & mth.Name & vbCrLf _
	& "Creado: " & mth.Created, , "Formulario Thing"
End Sub

' Botón "Invertir el nombre de Thing"
Private Sub Command2_Click()
	mth.ReverseName
	' Clic en "Mostrar Thing"
	Command1.Value = True
End Sub

' Botón "Crear nuevo Thing"
Private Sub Command3_Click()
	Set mth = New Thing
	mth.Name = InputBox( _
	"Escriba un nombre para el nuevo Thing")
End Sub

' Botón "Thing temporal".
Private Sub Command4_Click()
	Dim thTemp As New Thing
	thTemp.Name = InputBox( _
	"Escriba un nombre para el Thing temporal")
End Sub
 

 

Ejecución del proyecto

Presione F5 para ejecutar el proyecto. Si observa el código del procedimiento de evento Form_Load, podrá ver que se utiliza el operador New para crear un objeto Thing. A la variable mth se le asigna una referencia a este objeto Thing.

Verá un cuadro de entrada que le pide un nombre para el objeto Thing. Cuando escribe un nombre y presiona ENTRAR, el valor devuelto se asigna a la propiedad Name del objeto Thing.

Mostrar Thing

Puede comprobar que se ha asignado la propiedad Name si hace clic en el primer botón, “Mostrar Thing”, que presenta un cuadro de mensajes con todas las propiedades del objeto Thing.

Invertir el nombre de Thing

Haga clic en el segundo botón, “Invertir el nombre de Thing”. Este botón llama al método ReverseName para invertir el orden de las letras del nombre del objeto Thing y después activa el primer botón para presentar los valores actualizados de las propiedades.

Crear nuevo Thing

Haga clic en el botón “Crear nuevo Thing” para destruir el objeto Thing existente y crear uno nuevo. (O bien, como escribió realmente, para crear un nuevo Thing y después destruir el antiguo.)

El operador New hace que se cree un nuevo objeto Thing, de modo que verá el MsgBox presentado por el evento Initialize del nuevo objeto Thing. Cuando hace clic en Aceptar se coloca una referencia al nuevo objeto Thing en la variable mth del nivel de formulario.

Así se elimina la referencia del objeto Thing antiguo. Como no hay más referencias a él, se destruye y aparece el cuadro de mensajes de su evento Terminate. Cuando hace clic en Aceptar, la instrucción InputBox solicita un nombre para el nuevo objeto Thing.

Thing temporal

El cuarto botón demuestra otro aspecto de la duración del objeto. Cuando haga clic en él, se le pedirá un nombre para el objeto Thing temporal.

Pero espere, porque todavía no existe un objeto Thing temporal. Si no ha visto el cuadro de mensajes del evento Initialize, ¿cómo puede asignarle un nombre?

Como la variable thTemp se declaró As New, se creará un objeto Thing en el momento en que se invoca una de sus propiedades o de sus métodos. Esto ocurrirá cuando se asigne el valor devuelto por InputBox a la propiedad Name. Escriba un nombre y haga clic en Aceptar en el cuadro de entrada.

A continuación verá el cuadro de mensajes Inicializar Thing, que muestra que la propiedad Name está vacía. Cuando hace clic en Aceptar para cerrar el cuadro de mensajes, se asigna el valor de la instrucción InputBox a la propiedad Name. Ésta es mucha actividad para una única línea de código.

Naturalmente, en cuanto haya hecho esto termina el procedimiento de evento Click y la variable thTemp pierde su alcance. Se libera la referencia del objeto Thing temporal, por lo que verá el cuadro de mensajes Terminar Thing. Observe que contiene el nombre que escribió.

Cada vez que haga clic en este botón se creará otro objeto Thing temporal, se le asignará un nombre y se destruirá.

Cierre del programa

Cierre el programa con un clic en el botón de cierre del formulario. No utilice el botón Terminar de la barra de herramientas. Cuando el programa se cierra, se destruye Form1. La variable mth pierde su alcance y Visual Basic libera la referencia al objeto Thing. No quedan referencias al objeto Thing, por lo que se destruye y se presenta el cuadro de mensajes del evento Terminate.

Vuelva a ejecutar el programa, pero esta vez termínelo mediante el botón Terminar de la barra de herramientas. Observe que no se presenta el cuadro de mensajes Terminar del objeto Thing.

Es importante recordar que si termina un programa con el botón Terminar o con una instrucción End desde el código, detiene el programa inmediatamente sin ejecutar los eventos Terminate de los objetos. Siempre es mejor cerrar el programa con todos los formularios descargados.

Puede encontrar útil ejecutar el ejemplo con la tecla F8 para recorrer el código de línea en línea. Ésta es una buena manera de entender el orden de los eventos que tienen lugar en la creación y destrucción de un objeto.

Importante En una aplicación real, los eventos Initialize y Terminate no deben contener cuadros de mensaje ni ningún código que permita procesar los mensajes en Windows. En general, es mejor utilizar instrucciones Debug.Print cuando depure la duración de los objetos.

Para obtener más información Los formularios y los controles son diferentes del resto de los objetos, como se describe en “Duración de los formularios de Visual Basic”.

Puede encontrar más detalles acerca de las clases y los módulos de clase en “Agregar propiedades y métodos a una clase” y “Agregar eventos a una clase”.


5 - Depuración de los módulos de clase

La depuración de los módulos de clase difiere ligeramente de la depuración de los programas normales, ya que un error en una propiedad o un método de un módulo de clase siempre actúa como un error controlado. (Es decir, siempre hay un procedimiento en la pila de llamadas que puede controlar el error, a saber: el procedimiento que invocó la propiedad o el método del módulo de clase.)

Visual Basic compensa esta diferencia con la opción de interceptación de errores Interrupción en módulos de clases, además de las opciones antiguas Interrupción en errores no controlables y Modo de interrupción en todos.

Nota Puede establecer el estado Interceptación de errores predeterminada en la ficha General del cuadro de diálogo Opciones, disponible en el menú Herramientas. La opción seleccionada afecta a la sesión actual y se convierte en la predeterminada para todas las instancias posteriores de Visual Basic. Para cambiar el valor sólo para la sesión actual, sin afectar a la opción predeterminada, seleccione Alternar en el menú contextual de la ventana Código (que aparece al hacer clic con el botón secundario del mouse en la ventana Código)

Por ejemplo, suponga que el módulo de clase Class1 contiene el código siguiente:

Public Sub Oops()
	Dim intOops As Integer
	intOops = intOops / 0
End Sub 

 

Ahora suponga que un procedimiento de otro módulo de clase, formulario o módulo estándar invoca al miembro Oops:

Private Sub Command1_Click()
	Dim c1 As New Class1
	c1.Oops
End Sub 

 

Si la opción de interceptación de errores está establecida a Interrupción en errores no controlables, la ejecución no se detendrá en la división por cero. En su lugar, el error se producirá en el procedimiento que llama, Command1_Click. La ejecución se detendrá en la llamada al método Oops.

Podría utilizar Interrupción en todos los errores para detenerse en la división por cero, pero esta opción no es la más conveniente en la mayoría de las situaciones, ya que se detiene en todos los errores, incluso en los errores para los que ha escrito código de control.

Es más lógico activar Interrupción en módulo de clases:

Sugerencia Cuando llega a un punto de interrupción con las opciones Interrupción en módulos de clases o Interrupción en errores no controlables, puede recorrer el módulo o saltar el error si presiona ALT+F8 o ALT+F5 (dentro del código de control de errores o dentro del código que llamó al procedimiento en que se produjo el error).

Para obtener más información La depuración se describe en detalle en “Depurar el código y controlar los errores”.


6 - Duración de los formularios de Visual Basic

Puesto que son visibles para el usuario, los formularios y los controles tienen un ciclo de vida diferente del resto de los objetos. Por ejemplo, un formulario no se cierra sólo por liberar todas las referencias al mismo. Visual Basic mantiene una colección global con todos los formularios de su proyecto y sólo quita un formulario de dicha colección cuando lo descarga.

De forma parecida, Visual Basic mantiene una colección con todos controles de cada formulario. Puede cargar y descargar controles desde las matrices de controles, pero simplemente liberar todas las referencias a un control no es suficiente para destruirlo.

Para obtener más información Las colecciones Forms y Controls se describen en “Colecciones de Visual Basic”, anteriormente en este tema.

Estados por los que pasa un formulario de Visual Basic

Un formulario de Visual Basic pasa normalmente por cuatro estados en su ciclo de vida:

Un formulario de Visual Basic pasa normalmente por cuatro estados en su ciclo de vida:

Hay un quinto estado por el que un formulario puede pasar en algunas circunstancias: descargado y sin referencias, mientras se sigue haciendo referencia a un control.

En este tema se describen estos estados y las transiciones entre ellos.

Creado, pero no cargado

El comienzo de este estado viene marcado por el evento Initialize. El código del procedimiento de evento Form_Initialize es, por tanto, el primer código que se ejecuta cuando se crea un formulario.

En este estado, el formulario existe como objeto, pero no tiene ventana. No existe todavía ninguno de sus controles. Los formularios siempre pasan por este estado, aunque por poco tiempo.

Por ejemplo, si ejecuta Form1.Show, se creará el formulario y se ejecutará Form_Initialize; en cuanto termine Form_Initialize, se cargará el formulario, que es el estado siguiente.

Lo mismo ocurre si especifica un formulario como Objeto inicial, en la ficha General del cuadro de diálogo Propiedades del proyecto (que se encuentra disponible en el menú Proyecto). El formulario especificado como objeto inicial se crea al iniciar el proyecto y se carga y se muestra de forma inmediata

Nota Puede forzar que el formulario se cargue desde Form_Initialize si invoca el método Show o las propiedades y los métodos integrados, como se describe a continuación.

Creado, pero no cargado

Por el contrario, el código siguiente crea una instancia de Form1 sin avanzar hasta el estado de cargado:

Dim frm As Form1
Set frm = New Form1

En cuanto termina Form_Initialize, los únicos procedimientos que puede ejecutar sin forzar la carga del formulario son los procedimientos Sub, Function y Property que agregó en la ventana de código del formulario. Por ejemplo, puede agregar a Form1 el método siguiente:

Public Sub ANewMethod ()
	Debug.Print "Ejecutando ANewMethod"
End Sub 

Puede llamar a este método mediante la variable frm (es decir, frm.ANewMethod) sin forzar el paso del formulario al siguiente estado. De forma similar, podría llamar a ANewMethod para crear el formulario:

Dim frm As New Form1
frm.ANewMethod

Puesto que frm se declara As New, el formulario no se crea hasta la primera vez que utilice la variable en el código: en este caso, cuando se invoca ANewMethod. Después de ejecutar el código anterior, el formulario se mantiene creado, pero no cargado.

Nota Ejecutar Form1.ANewMethod sin declarar una variable formulario tiene el mismo efecto que el ejemplo anterior. Como se explica en “Personalización de las clases Form”, Visual Basic crea una variable global oculta por cada clase de formulario. Dicha variable tiene el mismo nombre que la clase; es como si Visual Basic declarara Public Form1 As New Form1.

Puede ejecutar tantas propiedades y métodos personalizados como desee sin forzar la carga del formulario. Sin embargo, en el momento en que tenga acceso a una de las propiedades integradas del formulario o a cualquier control del formulario, el formulario pasa al siguiente estado.

Nota Puede que le ayude pensar que un formulario se compone de dos partes: una parte de código y una parte visual. Antes de cargar el formulario, sólo está en memoria la parte de código. Puede invocar tantos procedimientos como desee en la parte de código sin cargar la parte visual del formulario.

El único estado por el que pasan todos los formularios

Creado, pero no cargado es el único estado por el que pasan todos los formularios. Si la variable frm de los ejemplos anteriores se establece a Nothing, como se muestra a continuación, el formulario se destruirá antes de pasar a su siguiente estado:

Dim frm As New Form1
frm.ANewMethod
Set frm = Nothing ' Se destruye el formulario

Este uso de los formularios no es mejor que los módulos de clase, ya que la gran mayoría de los formularios pasan al siguiente estado.

Cargado, pero no mostrado

El evento que marca el comienzo de este estado es el evento Load, que ya conoce. El código del procedimiento de evento Form_Load se ejecuta en cuanto el formulario entra en el estado cargado.

Cuando se inicia el procedimiento de evento Form_Load, se han creado y cargado todos los controles del formulario y el formulario tiene una ventana completa, con controlador de ventana (hWnd) y contexto de dispositivo (hDC), aunque dicha ventana no se muestre todavía.

Todo formulario que sea visible tiene que cargarse antes.

Muchos formularios pasan automáticamente del estado Creado, pero no cargado al estado Cargado, pero no mostrado. Un formulario se carga automáticamente si:

 

Formularios que nunca se muestran

En los dos primeros casos enumerados antes, el formulario pasará directamente al estado visible en cuanto termine Form_Load. En los dos últimos casos, el formulario seguirá cargado, pero no mostrado.

Durante mucho tiempo ha sido una práctica común de programación en Visual Basic cargar un formulario pero no mostrarlo nunca. Esto se puede hacer por varios motivos:

 

Nota Con la Edición profesional o la Edición empresarial, puede crear componentes ActiveX (anteriormente llamados servidores OLE), que suelen ofrecer una mejor funcionalidad que los controles para utilizar código sin parte visual. Consulte Creación de componentes ActiveX en la Guía de herramientas componentes.

Vuelta a casa

Los formularios vuelven del estado visible al estado cargado cuando se les oculta. Sin embargo, la vuelta al estado cargado no vuelve a ejecutar el evento Load. Form_Load se ejecuta sólo una vez dentro del ciclo de vida de un formulario.

Mostrado

En cuanto un formulario se hace visible, el usuario puede interactuar con él. Después, se puede ocultar y mostrar el formulario tantas veces como desee antes de descargarlo finalmente.

Interludio: preparación para la descarga

Un formulario puede estar oculto o visible cuando se descarga. Si no está explícitamente oculto, permanece visible hasta que se descarga.
El último evento que el formulario recibe antes de descargarse es el evento Unload. Sin embargo, antes de que esto ocurra, recibe un evento muy importante llamado QueryUnload. QueryUnload le ofrece la oportunidad de detener la descarga del formulario. Si hay datos que quizás desee guardar el usuario, éste es el momento de pedir al usuario que guarde o descarte sus modificaciones.

Importante Si establece el argumento Cancel de QueryUnload a True, detendrá la descarga del formulario, lo que deniega la instrucción Unload.

Una de las características más eficaces de este evento es que le informa de cómo se ha provocado la descarga: por un clic del usuario en el botón de cierre por la ejecución de una instrucción Unload por el cierre de la aplicación o por el cierre de Windows. De esta forma, QueryUnload le permite ofrecer al usuario la posibilidad de cancelar el cierre del formulario, al tiempo que le permite cerrar el formulario desde el código cuando sea necesario.

Importante En algunas circunstancias, los formularios no reciben el evento QueryUnload: si utiliza la instrucción End para terminar el programa o si hace clic en el botón Terminar (o selecciona Terminar en el menú Ejecutar) dentro del entorno de desarrollo.

Para obtener más información Consulte “Evento QueryUnload” en la Referencia del lenguaje de los Libros en pantalla.

Vuelta al estado Creado, pero no cargado

Cuando se descarga el formulario, Visual Basic lo quita de la colección Forms. A menos que mantenga activada alguna variable con una referencia a dicho formulario, el formulario se destruirá y se liberarán su memoria y recursos.

Si mantiene una referencia del formulario en alguna variable, como en la variable global oculta descrita en “Personalización de las clases Form”, el formulario vuelve al estado Creado, pero no cargado. El formulario ya no tiene ventana y sus controles no existen.

El objeto sigue utilizando recursos y memoria. Siguen existiendo todos los datos de las variables de nivel de módulo contenidos en la parte de código del formulario. (Sin embargo, desaparecen las variables Static de los procedimientos de evento.)

Puede utilizar esta referencia que ha guardado para llamar a los métodos y las propiedades que agregó al formulario, pero si invoca los miembros incorporados del formulario o si tiene acceso a sus controles, el formulario volverá a cargarse y se ejecutará el evento Form_Load.

Liberación completa de memoria y recursos

La única manera de liberar toda la memoria y los recursos es descargar el formulario y establecer todas sus referencias a Nothing. La referencia que más se suele pasar por alto al realizar esta tarea es la variable global oculta mencionada anteriormente. Si en algún momento ha hecho referencia al formulario por su nombre de clase (como aparece en la propiedad Name de la ventana Propiedades), ha utilizado la variable global oculta. Para liberar memoria del formulario, tiene que establecer esta variable a Nothing. Por ejemplo:

Set Form1 = Nothing

 

El formulario recibirá un evento Terminate justo antes de destruirse.

Sugerencia Muchos programadores profesionales evitan el uso de la variable global oculta y prefieren declarar sus propias variables de formulario para administrar la duración de los formularios (por ejemplo, Dim dlgAbout As New frmAboutBox).

Nota Ejecutar la instrucción End descarga todos los formularios y establece todas las variables de objeto del programa a Nothing. Sin embargo, es una manera muy brusca de terminar los programas. Ninguno de los formularios recibirá los eventos QueryUnload, Unload o Terminate, y los objetos que creó no recibirán los eventos Terminate.

Descargado y sin referencias, pero un control tiene referencias

Para llegar a este estado, tiene que descargar y liberar el formulario al tiempo que mantiene una referencia a alguno de sus controles. Si esto le parece una tontería, no dude que lo es.

Dim frm As New Form1
Dim obj As Object
frm.Show vbModal
' Cuando se descarta el formulario modal, guarda una 
' referencia de uno de sus controles.
Set obj = frm.Command1
Unload frm
Set frm = Nothing 

 

El formulario se descarga y se liberan todas las referencias a él. Sin embargo, todavía queda una referencia a uno de sus controles y esto impide que la parte de código del formulario libere la memoria utilizada. Si invoca cualquiera de las propiedades o métodos de este control, se volverá a cargar el formulario:

obj.Caption = "Vuelta a la vida"

Los valores de las variables de nivel de módulo serán los mismos de antes, pero los valores de las propiedades de todos los controles recuperarán sus valores predeterminados, como si el formulario se cargara por primera vez. Se ejecutará el evento Form_Load.

Nota En algunas versiones anteriores de Visual Basic, el formulario no se volvía a inicializar completamente y Form_Load no se ejecutaba otra vez.

Nota No todos los formularios se comportan como los formularios de Visual Basic. Por ejemplo, Microsoft Forms proporcionado con Microsoft Office no tiene eventos Load y Unload; cuando estos formularios reciben los eventos Initialize, todos sus controles existen y están listos para su uso.

Para obtener más información Los formularios se describen en el tema “Diseñar un formulario” de “Formularios, controles y menús” y en el tema “Más acerca de los formularios” de “Crear la interfaz de usuario”.

 


7 - Módulos de clase y módulos estándar

Las clases difieren de los módulos estándar en la forma de almacenar sus datos. Nunca hay más de una copia de los datos de un módulo estándar. Esto significa que cuando una parte del programa modifica una variable pública de un módulo estándar y otra parte del programa la lee después, obtendrá el mismo valor.

Por otra parte, los datos de un módulo de clase existen por separado para cada instancia de la clase (es decir, por cada objeto creado a partir de la clase).

Según el mismo razonamiento, los datos de un módulo estándar tienen el alcance del programa; es decir, existen durante toda la ejecución del programa, mientras que los datos de cada instancia de un módulo de clase sólo existen durante la duración del objeto; se crean cuando se crea el objeto y se destruyen cuando se destruye el objeto.

Por último, las variables declaradas Public en los módulos estándar son visibles desde cualquier parte del proyecto, mientras que sólo puede tener acceso a las variables Public de los módulos de clase si dispone de una variable de objeto que contenga una referencia a una instancia concreta de una clase.

Todo lo anterior también se aplica a los procedimientos públicos de los módulos estándar y de los módulos de clase. Todo esto se ilustra en el ejemplo siguiente. Puede ejecutar este código si abre un proyecto Exe estándar y utiliza el menú Proyecto para agregar un módulo y un módulo de clase.

 

Coloque el código siguiente en Module1:

' La siguiente es una propiedad de los objetos Class1.
Public Comment As String

' El siguiente es un método de los objetos Class1.
Public Sub ShowComment()
	MsgBox Comment, , gstrVisibleEverywhere
End Sub
	
Coloque el código siguiente en Module1:
' El código del módulo estándar es global.
Public gstrVisibleEverywhere As String

Public Sub CallableAnywhere(ByVal c1 As Class1)
	' La línea siguiente modifica una variable global
	' (propiedad) de una instancia de Class1. Sólo 
	' afecta al objeto en particular pasado a este 
	' procedimiento.
	c1.Comment = "Tocado por una función global."
End Sub
 

 

Coloque dos botones de comando en Form1 y agregue el código siguiente a Form1:

Private mc1First As Class1
Private mc1Second As Class1

Private Sub Form_Load()
	' Crea dos instancias de Class1.
	Set mc1First = New Class1
	Set mc1Second = New Class1
	gstrVisibleEverywhere = "Datos de cadena global"
End Sub

Private Sub Command1_Click()
	Call CallableAnywhere(mc1First)
	mc1First.ShowComment
End Sub

Private Sub Command2_Click()
	mc1Second.ShowComment
End Sub
 

 

Presione F5 para ejecutar el proyecto. Cuando se carga, Form1 crea dos instancias de Class1, cada una con sus propios datos. Form1 establece también el valor de la variable global gstrVisibleEverywhere.

Haga clic en Command1, que llama al procedimiento global y pasa una referencia al primer objeto Class1. El procedimiento global establece la propiedad Comment y después Command1 llama al método ShowComment para presentar los datos del objeto.

Como se puede ver en la figura 9.6, el cuadro de mensajes resultante demuestra que el procedimiento global CallableAnywhere establece la propiedad Comment del objeto que se le pasa y que la cadena global es visible desde dentro de Class1.

Importante Evite que el código de sus clases dependa de datos globales, es decir, de variables públicas de módulos estándar. Pueden existir simultáneamente varias instancias de una clase y todos estos objetos comparten los datos globales del programa.
Además, el empleo de variables globales en el código de los módulos de clase, no cumple con el concepto de encapsulamiento de la programación orientada a objetos, puesto que los objetos creados a partir de dichas clases no contienen todos sus datos.

 

Datos estáticos de clase

Puede haber ocasiones en las que quiera que todos los objetos creados a partir de un módulo de clase compartan un único elemento de datos. A esto se le suele llamar datos estáticos de clase.
No puede implementar datos de clase realmente estáticos en los módulos de clase de Visual Basic. Sin embargo, puede simularlo mediante procedimientos Property que establezcan y devuelvan el valor de un miembro de datos Public de un módulo estándar, como en el siguiente fragmento de código:

' Propiedad de sólo lectura que devuelve el 
' nombre de la aplicación.
Property Get CommonString() As String
	' La variable gstrVisibleEverywhere se almacena en 
	' un módulo estándar y se declara Public.
	CommonString = gstrVisibleEverywhere
End Property 

 

Nota No puede utilizar la palabra clave Static para las variables de nivel de módulo en los módulos de clase. La palabra clave Static sólo se puede utilizar dentro de procedimientos.

Es posible simular datos estáticos de clase que no sean de sólo lectura mediante un procedimiento Property Let (o Property Set si la propiedad contiene una referencia de objeto) que asigne un nuevo valor al miembro de datos del módulo estándar. Sin embargo, utilizar variables globales de esta forma no cumple con el concepto de encapsulamiento y no se recomienda.

Por ejemplo, la variable gstrVisibleEverywhere puede establecerse desde cualquier parte del proyecto, incluso desde código no perteneciente a la clase que tiene la propiedad CommonString. Esto puede conducir a situaciones de error en el programa.

Para obtener más información Los datos globales de los componentes ActiveX requieren un tratamiento diferente al de los programas normales. Si tiene la Edición profesional o la Edición empresarial de Visual Basic, vea el tema “Módulos estándar frente a módulos de clase” en “Principios generales del diseño de componentes”, de la sección Crear componentes ActiveX de la Guía de herramientas componentes.


Volver al índice

 

 


Buscar en Recursos vb