5. Interfaces gráficas con VB.NET y VS.NET
En este artículo nos proponemos mostrar cómo crear interfaces gráficas con VB.NET. En primer lugar, veremos cuáles son las clases básicas de la plataforma .NET que nos permiten crear una interfaz gráfica. En un primer momento, no utilizaremos ninguna herramienta de generación automática. Luego utilizaremos Visual Studio.NET (VS.NET), una herramienta de desarrollo de Microsoft que facilita el desarrollo de aplicaciones con los lenguajes .NET y, en particular, la creación de interfaces gráficas. La versión de VS.NET que se utiliza es la versión en inglés.
5.1. Conceptos básicos de las interfaces gráficas
5.1.1. Una ventana simple
Consideremos el siguiente código:
' opciones
Option Strict On
Option Explicit On
' espacios de nombres
Imports System
Imports System.Drawing
Imports System.Windows.Forms
' la clase formulario
Public Class Form1
Inherits Form
' el fabricante
Public Sub New()
' título de la ventana
Me.Text = "Mon premier formulaire"
' dimensiones de la ventana
Me.Size = New System.Drawing.Size(300, 100)
End Sub
' función de prueba
Public Shared Sub Main(ByVal args() As String)
' se muestra el formulario
Application.Run(New Form1)
End Sub
End Class
El código anterior se compila y luego se ejecuta
La ejecución muestra la siguiente ventana:

Una interfaz gráfica suele derivarse de la clase base System.Windows.Forms.Form:
La clase base Form define una ventana básica con botones para cerrar, ampliar o reducir, un tamaño ajustable, etc., y maneja los eventos en estos objetos gráficos. Aquí especializamos la clase base asignándole un título y su ancho (300) y alto (100). Esto se hace en su constructor:
Public Sub New()
' título de la ventana
Me.Text = "Mon premier formulaire"
' dimensiones de la ventana
Me.Size = New System.Drawing.Size(300, 100)
End Sub
El título de la ventana se establece mediante la propiedad Text y las dimensiones mediante la propiedad Size. Size se define en el espacio de nombres System.Drawing y es una estructura. El procedimiento Main inicia la aplicación gráfica de la siguiente manera:
Application.Run(New Form1)
Se crea y se muestra un formulario de tipo Form1; luego, la aplicación se pone a la espera de los eventos que ocurren en el formulario (clics, movimientos del ratón, etc.) y ejecuta aquellos que el formulario maneja. En este caso, nuestro formulario no maneja ningún otro evento más que los que maneja la clase base Form (clics en los botones de cierre, maximizar/minimizar, cambio de tamaño de la ventana, desplazamiento de la ventana, etc.).
5.1.2. Un formulario con un botón
Ahora agreguemos un botón a nuestra ventana:
' opciones
Option Strict On
Option Explicit On
' espacios de nombres
Imports System
Imports System.Drawing
Imports System.Windows.Forms
' la clase del formulario
Public Class Form1
Inherits Form
' atributos
Private cmdTest As Button
' el constructor
Public Sub New()
' el título
Me.Text = "Mon premier formulaire"
' las dimensiones
Me.Size = New System.Drawing.Size(300, 100)
' un botón
' creación
Me.cmdTest = New Button
' posición
cmdTest.Location = New System.Drawing.Point(110, 20)
' tamaño
cmdTest.Size = New System.Drawing.Size(80, 30)
' etiqueta
cmdTest.Text = "Test"
' gestor de eventos
AddHandler cmdTest.Click, AddressOf cmdTest_Click
' agregar botón al formulario
Me.Controls.Add(cmdTest)
End Sub
' gestor de eventos
Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
' Se hizo clic en el botón; se notifica
MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
' función de prueba
Public Shared Sub Main(ByVal args() As String)
' se muestra el formulario
Application.Run(New Form1)
End Sub
End Class
Hemos agregado un botón al formulario:
' un botón
' creación
Me.cmdTest = New Button
' posición
cmdTest.Location = New System.Drawing.Point(110, 20)
' tamaño
cmdTest.Size = New System.Drawing.Size(80, 30)
' etiqueta
cmdTest.Text = "Test"
' gestor de eventos
AddHandler cmdTest.Click, AddressOf cmdTest_Click
' agregar botón al formulario
Me.Controls.Add(cmdTest)
La propiedad Location establece las coordenadas (110,20) del punto superior izquierdo del botón mediante una estructura Point. El ancho y la altura del botón se establecen en (80,30) mediante una estructura Size. La propiedad Text del botón permite establecer el texto del botón. La clase «botón» tiene un evento Click definido de la siguiente manera:
donde EventHandler es una función «delegada» con la siguiente firma:
Esto significa que el manejador del evento [Click] del botón deberá tener la firma del delegado [EventHandler]. En este caso, al hacer clic en el botón cmdTest, se llamará al método cmdTest_Click. Este se define de la siguiente manera, de acuerdo con el modelo anterior EventHandler:
' gestor de eventos
Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
' se hizo clic en el botón — se indica
MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
Simplemente se muestra un mensaje:

La clase se compila y se ejecuta:
dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
dos>frm2
La clase MessageBox sirve para mostrar mensajes en una ventana. Aquí hemos utilizado el constructor
Overloads Public Shared Function Show(ByVal owner As IWin32Window,ByVal text As String,ByVal caption As String,ByVal buttons As MessageBoxButtons,ByVal icon As MessageBoxIcon) As DialogResult
text | el mensaje que se mostrará |
caption | el título de la ventana |
buttons | los botones que aparecen en la ventana |
icono | el ícono que aparece en la ventana |
El parámetro buttons puede tomar valores de entre las siguientes constantes:
constante | botones |
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() |
El parámetro icon puede tomar valores de entre las siguientes constantes:
![]() | ídem Stop | ||
ídem Advertencia | ![]() | ||
Lo mismo que Asterisk | ![]() | ||
![]() | ídem Mano | ||
![]() |
El método Show es un método estático que devuelve un resultado de tipo System.Windows.Forms.DialogResult, que es una enumeración:
public enum System.Windows.Forms.DialogResult
{
Abort = 0x00000003,
Cancel = 0x00000002,
Ignore = 0x00000005,
No = 0x00000007,
None = 0x00000000,
OK = 0x00000001,
Retry = 0x00000004,
Yes = 0x00000006,
}
Para saber qué botón presionó el usuario para cerrar la ventana de tipo MessageBox, escribiremos:
dim res as DialogResult=MessageBox.Show(..)
if res=DialogResult.Yes then
' pulsó el botón «Sí»
...
end if
5.2. Crear una interfaz gráfica con Visual Studio.NET
Retomamos algunos de los ejemplos vistos anteriormente y ahora los creamos con Visual Studio.NET.
5.2.1. Creación inicial del proyecto
- Inicie VS.NET y seleccione la opción Fichier/Nouveau/Projet

- Ingrese las características de su proyecto
![]() |
- Seleccione el tipo de proyecto que desea crear; en este caso, un proyecto VB.NET (1)
- selecciona el tipo de aplicación que deseas crear; en este caso, una aplicación de Windows (2)
- Indica en qué carpeta quieres colocar la subcarpeta del proyecto (3)
- Indica el nombre del proyecto (4). Este será también el nombre de la carpeta que contendrá los archivos del proyecto
- El nombre de esta carpeta se muestra en (5)
- A continuación, se crean varias carpetas y archivos dentro de la carpeta i4:
subcarpetas de la carpeta proyecto1 ![]() |
De estos archivos, solo uno es relevante: el archivo form1.cs, que es el archivo de origen asociado al formulario creado por VS.NET. Volveremos a ello más adelante.
5.2.2. Las ventanas de la interfaz de VS.NET
La interfaz de VS.NET ahora muestra algunos elementos de nuestro proyecto i4:
Tenemos una ventana de diseño de la interfaz gráfica:
![]() |
Al seleccionar controles de la barra de herramientas (caja de herramientas 2) y colocarlos en el área de trabajo de la ventana (1), podemos construir una interfaz gráfica. Si pasamos el ratón sobre la «caja de herramientas», esta se amplía y muestra una serie de controles:

Por el momento, no utilizamos ninguno. Todavía en la pantalla de VS.NET, encontramos la ventana del explorador de soluciones «Explorer Solution»:

Al principio, apenas utilizaremos esta ventana. Muestra todos los archivos que conforman el proyecto. Solo nos interesa uno de ellos: el archivo fuente de nuestro programa, en este caso Form1.vb. Al hacer clic con el botón derecho sobre Form1.vb, aparece un menú que permite acceder al código fuente de nuestra interfaz gráfica (Mostrar código) o a la propia interfaz gráfica (Diseñador de vistas):

Se puede acceder a estas dos entidades directamente desde la ventana «Explorador de soluciones»:
![]() | ![]() |
Las ventanas abiertas se «acumulan» en la ventana principal de diseño:

Aquí, Form1.vb[Design] se refiere a la ventana de diseño y Form1.vb a la ventana de código. Basta con hacer clic en una de las pestañas para pasar de una ventana a la otra. Otra ventana importante que aparece en la pantalla de VS.NET es la ventana de propiedades:

Las propiedades que se muestran en la ventana son las del control actualmente seleccionado en la ventana de diseño gráfico. Se puede acceder a diferentes ventanas del proyecto mediante el menú Affichage:

Ahí se encuentran las ventanas principales que acabamos de describir, así como sus atajos de teclado.
5.2.3. Ejecución de un proyecto
Aunque no hayamos escrito ningún código, ya tenemos un proyecto ejecutable. Ejecútelo con F5 o Déboguer/Démarrer. Aparecerá la siguiente ventana:

Esta ventana se puede ampliar, minimizar, redimensionar y cerrar.
5.2.4. El código generado por VS.NET
Veamos el código (Affichage/Code) de nuestra aplicación:
Public Class Form1
Inherits System.Windows.Forms.Form
#Región «Código generado por el Diseñador de Windows Forms»
Public Sub New()
MyBase.New()
'Esta llamada es requerida por el Diseñador de Windows Forms.
InitializeComponent()
'Agrega cualquier inicialización después de la llamada a InitializeComponent()
End Sub
'El método sobreescrito Dispose del formulario limpia la lista de componentes.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Requerido por el Diseñador de Windows Forms
Private components As System.ComponentModel.IContainer
'REMARQUE: el Diseñador de Windows Forms requiere el siguiente procedimiento
'Se puede modificar utilizando el Diseñador de Windows Form.
'No lo modifique utilizando el editor de código.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
Me.Text = "Form2"
End Sub
#Fin de región
End Class
Una interfaz gráfica deriva de la clase base System.Windows.Forms.Form:
La clase base Form define una ventana básica con botones para cerrar, ampliar o reducir, un tamaño ajustable, etc., y maneja los eventos en estos objetos gráficos. El constructor del formulario utiliza un método InitializeComponent en el que se crean e inicializan los controles del formulario.
Public Sub New()
MyBase.New()
'El Diseñador de Windows Forms requiere esta llamada.
InitializeComponent()
'Agrega cualquier inicialización después de la llamada a InitializeComponent()
End Sub
Cualquier otro trabajo que se deba realizar en el generador se puede hacer después de llamar a InitializeComponent. El método InitializeComponent
Private Sub InitializeComponent()
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 53)
Me.Name = "Form1"
Me.Text = "Form1"
End Sub
establece el título de la ventana «Form1», su ancho (292) y su alto (53). El título de la ventana se establece mediante la propiedad Text y las dimensiones mediante la propiedad Size. Size se define en el espacio de nombres System.Drawing y es una estructura. Para ejecutar esta aplicación, debemos definir el módulo principal del proyecto. Para ello, utilizamos la opción [Projets/Propriétés]:

En [Objet de démarrage], indicamos [Form1], que es el formulario que acabamos de crear. Para iniciar la ejecución, utilizamos la opción [Déboguer/Démarrer]:

5.2.5. Compilación en una ventana de DOS
Ahora, intentemos compilar y ejecutar esta aplicación en una ventana de DOS:
dos>dir form1.vb
14/03/2004 11:53 514 Form1.vb
dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb
vbc : error BC30420: 'Sub Main' est introuvable dans 'Form1'.
El compilador indica que no encuentra el procedimiento [Main]. De hecho, VS.NET no lo generó. Sin embargo, ya lo hemos visto en los ejemplos anteriores. Tiene la siguiente forma:
Shared Sub Main()
' se inicia la aplicación
Application.Run(New Form1) ' où Form1 est le formulaire
End Sub
Agreguemos al código de [Form1.vb] el código anterior y recompilemos:
dos>vbc /r:system.dll /r:system.windows.forms.dll form2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
Form2.vb(41) : error BC30451: Le nom 'Application' n'est pas déclaré.
Application.Run(New Form2)
~~~~~~~~~~~
En esta ocasión, el nombre [Application] no es reconocido. Esto simplemente significa que no hemos importado su espacio de nombres [System.Windows.Forms]. Agreguemos la siguiente instrucción:
y luego recompilemos:
Esta vez sí funciona. Ejecutemos:

Se crea y se muestra un formulario de tipo Form1. Se puede evitar agregar el procedimiento [Main] utilizando la opción /m del compilador, que permite especificar la clase que se debe ejecutar en caso de que esta herede de System.Windows.Form:
La opción /m:form2 indica que la clase que se debe ejecutar es la clase con el nombre [form2].
5.2.6. Manejo de eventos
Una vez que se muestra el formulario, la aplicación comienza a detectar los eventos que ocurren en el formulario (clics, movimientos del ratón, etc.) y ejecuta aquellos que el formulario maneja. En este caso, nuestro formulario no maneja ningún otro evento más que los que maneja la clase base Form (clics en los botones de cierre, maximizar/minimizar, cambio de tamaño de la ventana, desplazamiento de la ventana, etc.). El formulario generado utiliza un atributo components que no se utiliza en ninguna parte. El método dispose tampoco sirve para nada aquí. Lo mismo ocurre con algunos espacios de nombres (Collections, ComponentModel, Data) que se utilizan y con el definido para el proyecto projet1. Por lo tanto, en este ejemplo, el código se puede simplificar de la siguiente manera:
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Public Class Form1
Inherits System.Windows.Forms.Form
' constructor
Public Sub New()
' construcción del formulario con sus componentes
InitializeComponent()
End Sub
Private Sub InitializeComponent()
' tamaño de la ventana
Me.Size = New System.Drawing.Size(300, 300)
' título de la ventana
Me.Text = "Form1"
End Sub
Shared Sub Main()
' Se inicia la aplicación
Application.Run(New Form1)
End Sub
End Class
5.2.7. Conclusión
Ahora aceptaremos tal cual el código generado por VS.NET y nos limitaremos a agregar el nuestro, especialmente para gestionar los eventos relacionados con los distintos controles del formulario.
5.3. Ventana con campo de entrada, botón y etiqueta
5.3.1. Diseño gráfico
En el ejemplo anterior, no habíamos colocado ningún componente en la ventana. Comenzamos un nuevo proyecto llamado interface2. Para ello, seguimos el procedimiento explicado anteriormente para crear un proyecto:

Ahora construyamos una ventana con un botón, una etiqueta y un campo de entrada:
![]() |
Los campos son los siguientes:
n.º | nombre | tipo | función |
1 | lblSaisie | Etiqueta | una descripción |
2 | txtSaisie | TextBox | un campo de entrada |
3 | btnAfficher | Botón | para mostrar en un cuadro de diálogo el contenido del campo de entrada txtSaisie |
Para crear esta ventana, se puede proceder de la siguiente manera: haga clic con el botón derecho del ratón en la ventana, fuera de cualquier componente, y seleccione la opción Properties para acceder a las propiedades de la ventana:

La ventana de propiedades aparecerá entonces a la derecha:

Cabe destacar algunas de estas propiedades:
para establecer el color de fondo de la ventana | |
para establecer el color de los dibujos o del texto en la ventana | |
para asociar un menú a la ventana | |
para asignar un título a la ventana | |
para establecer el tipo de ventana | |
para establecer el tipo de letra del texto en la ventana | |
para establecer el nombre de la ventana |
Aquí configuramos las propiedades Text y Name:
Campos de entrada y botones - 1 | |
frmSaisiesBoutons |
Con la barra «Caja de herramientas»
- seleccione los componentes que necesite
- arrástralos a la ventana y ajústalos a las dimensiones correctas
![]() | ![]() |
Una vez que hayas elegido el componente en la «caja de herramientas», usa la tecla «Esc» para ocultar la barra de herramientas, luego arrastra y ajuste el tamaño del componente. Haz lo mismo con los tres componentes necesarios: Label, TextBox, Button. Para alinear y ajustar correctamente el tamaño de los componentes, usa el menú Format: | ![]() |
El principio del formato es el siguiente:
La opción Align te permite alinear componentes | ![]() |
La opción «Make Same Size» permite que componentes tengan la misma altura o el mismo ancho: | ![]() |
La opción «Espaciado horizontal» permite, por ejemplo, alinear horizontalmente los componentes con espacios de igual tamaño entre ellos. Lo mismo con la opción «Espaciado vertical» para alinear verticalmente. La opción «Center in Form» permite centrar un componente horizontalmente o verticalmente en la ventana: | ![]() |
Una vez que los componentes estén correctamente colocados en la ventana, configura sus propiedades. Para ello, haz clic con el botón derecho en el componente y seleccione la opción Properties:
Seleccione el componente para acceder a su ventana de propiedades. En ella, modifica las siguientes propiedades: nombre: lblSaisie, texto: Campo de entrada
Seleccione el componente para abrir su ventana de propiedades. En ella, modifica las siguientes propiedades: nombre: txtSaisie, texto: no poner nada
nombre: cmdAfficher, texto: Mostrar
texto: Entradas y botones - 1 | ![]() |
Podemos ejecutar (Ctrl+F5) nuestro proyecto para tener una primera vista previa de la ventana en acción: | ![]() |
Cierra la ventana. Ahora nos queda escribir el procedimiento asociado al clic en el botón Afficher.
5.3.2. Manejo de eventos de un formulario
Veamos el código que generó el diseñador gráfico:
...
Public Class frmSaisiesBoutons
Inherits System.Windows.Forms.Form
' componentes
Private components As System.ComponentModel.Container = Nothing
Friend WithEvents btnAfficher As System.Windows.Forms.Button
Friend WithEvents lblsaisie As System.Windows.Forms.Label
Friend WithEvents txtsaisie As System.Windows.Forms.TextBox
' constructor
Public Sub New()
InitializeComponent()
End Sub
...
' inicialización de los componentes
Private Sub InitializeComponent()
...
End Sub
End Class
En primer lugar, cabe destacar la declaración especial de los componentes:
- la palabra clave `Friend` indica que la visibilidad del componente se extiende a todas las clases del proyecto
- la palabra clave WithEvents indica que el componente genera eventos. Ahora nos enfocaremos en cómo manejar estos eventos
Muestre la ventana de código del formulario (Ver/Código o F7):
![]() |
La ventana anterior presenta dos listas desplegables (1) y (2). La lista (1) es la lista de componentes del formulario:

La lista (2) es la lista de eventos asociados al componente seleccionado en (1):

Uno de los eventos asociados al componente aparece en negrita (en este caso, Click). Este es el evento predeterminado del componente. Para acceder al manejador de este evento en particular, se puede hacer doble clic en el componente en la ventana de diseño. VB.net genera automáticamente el esqueleto del manejador de eventos en la ventana de código y coloca al usuario en él. El acceso a los manejadores de los demás eventos se realiza en la ventana de código, seleccionando el componente en la lista (1) y el evento en (2). VB.net genera entonces el esqueleto del manejador de eventos o se posiciona sobre él si ya estaba generado:
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
End Sub
Por defecto, VB.net C_E asigna el nombre al manejador del evento E del componente C. Se puede cambiar este nombre si así lo deseamos. Sin embargo, no es recomendable hacerlo. Los desarrolladores de VB suelen conservar el nombre generado por VB, lo que garantiza la coherencia en la nomenclatura de todos los programas VB. La asociación del procedimiento btnAfficher_Click al evento Click del componente btnAfficher no se realiza mediante el nombre del procedimiento, sino mediante la palabra clave handles:
Handles btnAfficher.Click
En el ejemplo anterior, la palabra clave «handles» especifica que el procedimiento maneja el evento «Click» del componente btnAfficher. El manejador de eventos anterior tiene dos parámetros:
el objeto que origina el evento (en este caso, el botón) | |
un objeto EventArgs que detalla el evento que ocurrió |
No utilizaremos ninguno de estos parámetros aquí. Solo nos queda completar el código. En este caso, queremos mostrar un cuadro de diálogo con el contenido del campo txtSaisie:
Private Sub btnAfficher_Click(ByVal sender As Object, ByVal e As System.EventArgs)
' se muestra el texto que se ingresó en el campo de entrada TxtSaisie
MessageBox.Show("texte saisi= " + txtsaisie.Text, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
Si ejecutamos la aplicación, obtenemos lo siguiente:

5.3.3. Otro método para manejar los eventos
Para el botón btnAfficher, VS.NET generó el siguiente código:
Private Sub InitializeComponent()
...
'
'btnAfficher
'
Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
Me.btnAfficher.Name = "btnAfficher"
Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
Me.btnAfficher.TabIndex = 2
Me.btnAfficher.Text = "Afficher"
...
End Sub
...
' gestor de eventos al hacer clic en el botón cmdAfficher
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
End Sub
Podemos asociar el procedimiento btnAfficher_Click al evento Click del botón btnAfficher de otra manera:
Private Sub InitializeComponent()
...
'
'btnAfficher
'
Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
Me.btnAfficher.Name = "btnAfficher"
Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
Me.btnAfficher.TabIndex = 2
Me.btnAfficher.Text = "Afficher"
AddHandler btnAfficher.Click, AddressOf btnAfficher_Click
...
End Sub
...
' gestor de eventos al hacer clic en el botón cmdAfficher
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
End Sub
El procedimiento btnAfficher_Click ha perdido la palabra clave Handles, por lo que ya no está asociado al evento btnAfficher.Click. Ahora, esta asociación se realiza mediante la palabra clave AddHandler:
AddHandler btnAfficher.Click, AddressOf btnAfficher_Click
El código anterior, que se colocará en el procedimiento InitializeComponent del formulario, asocia al evento btnAfficher.Click el procedimiento denominado btnAfficher_Click. Además, el componente btnAfficher ya no necesita la palabra clave WithEvents:
Friend btnAfficher As System.Windows.Forms.Button
¿Cuál es la diferencia entre los dos métodos?
- La palabra clave `handles` solo permite asociar un evento a un procedimiento en el momento del diseño. El diseñador sabe de antemano que un procedimiento P debe manejar los eventos E1, E2, ... y escribe el código
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) handles E1, E2, ..., En
De hecho, es posible que un procedimiento maneje varios eventos.
- La palabra clave addhandler permite asociar un evento a un procedimiento en el momento de la ejecución. Esto resulta útil en un contexto de productor-consumidor de eventos. Un objeto genera un evento específico que puede ser de interés para otros objetos. Estos se suscriben al generador para recibir el evento (por ejemplo, una temperatura que ha superado un umbral crítico). Durante la ejecución de la aplicación, el generador del evento deberá ejecutar diferentes instrucciones:
donde E es el evento generado por el productor y Pi son los procedimientos que pertenecen a los distintos objetos consumidores de ese evento. Tendremos la oportunidad de volver a abordar una aplicación de productor-consumidor de eventos en un próximo capítulo.
5.3.4. Conclusión
A partir de los dos proyectos analizados, podemos concluir que, una vez construida la interfaz gráfica con VS.NET, el trabajo del desarrollador consiste en escribir los controladores de eventos que desea gestionar para dicha interfaz gráfica. A partir de ahora, solo presentaremos el código de estos.
5.4. Algunos componentes útiles
A continuación, presentamos diversas aplicaciones que utilizan los componentes más comunes con el fin de descubrir sus principales métodos y propiedades. Para cada aplicación, mostramos la interfaz gráfica y el código relevante, en particular los controladores de eventos.
5.4.1. Formulario Form
Comenzamos presentando el componente indispensable: el formulario en el que se colocan los componentes. Ya hemos presentado algunas de sus propiedades básicas. Aquí nos detendremos en algunos eventos importantes de un formulario.
El formulario se está cargando | |
El formulario se está cerrando | |
El formulario está cerrado |
El evento Load ocurre incluso antes de que se muestre el formulario. El evento Closing ocurre cuando el formulario se está cerrando. Este cierre aún se puede detener mediante programación. Creamos un formulario con el nombre Form1 sin ningún componente:

Procesamos los tres eventos anteriores:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' carga inicial del formulario
MessageBox.Show("Evt Load", "Load")
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
' el formulario se está cerrando
MessageBox.Show("Evt Closing", "Closing")
' se solicita confirmación
Dim réponse As DialogResult = MessageBox.Show("Voulez-vous vraiment quitter l'application", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If réponse = DialogResult.No Then
e.Cancel = True
End If
End Sub
Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
' el formulario se está cerrando
MessageBox.Show("Evt Closed", "Closed")
End Sub
Utilizamos la función MessageBox para recibir notificaciones de los distintos eventos. El evento Closing ocurrirá cuando el usuario cierre la ventana. | ![]() |
Entonces le preguntamos si realmente quiere salir de la aplicación: | ![]() |
Si responde «No», establecemos la propiedad Cancel del evento CancelEventArgs que el método recibió como parámetro. Si establecemos esta propiedad en False, se cancela el cierre de la ventana se cancela; de lo contrario, continúa: | ![]() |
5.4.2. Etiquetas y cuadros de entrada TextBox
Ya hemos visto estos dos componentes. Label es un componente de texto y TextBox un componente de campo de entrada. Su propiedad principal es Text, que designa ya sea el contenido del campo de entrada o el texto de la etiqueta. Esta propiedad es de lectura y escritura. El evento que se utiliza habitualmente para TextBox es TextChanged, que indica que el usuario ha modificado el campo de entrada. A continuación se muestra un ejemplo que utiliza el evento TextChanged para dar seguimiento a los cambios en un campo de entrada:
![]() |
n.º | tipo | nombre | función |
1 | TextBox | txtSaisie | campo de entrada |
2 | Etiqueta | lblControle | muestra el texto de 1 en tiempo real |
3 | Botón | cmdEffacer | para borrar los campos 1 y 2 |
4 | Botón | cmdQuitter | para salir de la aplicación |
El código relevante de esta aplicación es el de los controladores de eventos:
' se hace clic en el botón «Salir»
Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cmdQuitter.Click
' se hace clic en el botón «Salir»; se sale de la aplicación
Application.Exit()
End Sub
' modificación del campo txtSaisie
Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtSaisie.TextChanged
' el contenido de TextBox ha cambiado; se copia en la etiqueta lblControle
lblControle.Text = txtSaisie.Text
End Sub
' se hace clic en el botón «Borrar»
Private Sub cmdEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cmdEffacer.Click
' se borra el contenido del campo de entrada
txtSaisie.Text = ""
End Sub
' se ha presionado una tecla
Private Sub txtSaisie_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
Handles txtSaisie.KeyPress
' se verifica qué tecla se presionó
Dim touche As Char = e.KeyChar
If touche = ControlChars.Cr Then
MessageBox.Show(txtSaisie.Text, "Contrôle", MessageBoxButtons.OK, MessageBoxIcon.Information)
e.Handled = True
End If
End Sub
Cabe destacar la forma de cerrar la aplicación en el procedimiento cmdQuitter_Click: Application.Exit(). El siguiente ejemplo utiliza un TextBox de varias líneas:
![]() |
La lista de controles es la siguiente:
N.º | tipo | nombre | función |
1 | TextBox | txtMultiLignes | campo de entrada de varias líneas |
2 | TextBox | txtAjout | campo de entrada de una sola línea |
3 | Botón | btnAjouter | Agrega el contenido de 2 a 1 |
Para que un TextBox se convierta en multilínea, se configuran las siguientes propiedades del control:
para que admita varias líneas de texto | |
para indicar si el control debe tener barras de desplazamiento (Horizontal, Vertical, Both) o no (None) | |
si es igual a true, la tecla Enter pasará a la línea siguiente | |
si es igual a true, la tecla Tab generará un salto de tabulación en el texto |
El código relevante es el que procesa el clic en el botón [Ajouter] y el que procesa la modificación del campo de entrada [txtAjout]:
' posiblemente btnAjouter_Click
Private Sub btnAjouter_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnAjouter.Click
' se agrega el contenido de txtAjout al de txtMultilignes
txtMultilignes.Text &= txtAjout.Text
txtAjout.Text = ""
End Sub
' o bien txtAjout_TextChanged
Private Sub txtAjout_TextChanged1(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtAjout.TextChanged
' se establece el estado del botón «Agregar»
btnAjouter.Enabled = txtAjout.Text.Trim() <> ""
End Sub
5.4.3. listas desplegables ComboBox
![]() | ![]() |
Un componente ComboBox es una lista desplegable que incluye un campo de entrada: el usuario puede elegir un elemento en (2) o escribir texto en (1). Existen tres tipos de ComboBox definidos por la propiedad Estilo:
lista no desplegable con campo de edición | |
lista desplegable con campo de edición | |
lista desplegable sin campo de edición |
Por defecto, el tipo de un ComboBox es DropDown. Para conocer la clase ComboBox, escribe ComboBox en el índice de la ayuda (Aide/Index). La clase ComboBox tiene un único constructor:
Public Sub New() | crea un objeto ComboBox vacío |
Los elementos de ComboBox están disponibles en la propiedad Items:
Es una propiedad indexada, donde Items(i) designa el elemento i del combo. Supongamos que C es un combo y C.Items es su lista de elementos. Tenemos las siguientes propiedades:
número de elementos del combo | |
elemento i del combo | |
agrega el objeto o como último elemento del combo | |
agrega una tabla de objetos al final del combo | |
agrega el objeto o en la posición i del combo | |
elimina el elemento i del combo | |
elimina el objeto o del combo | |
elimina todos los elementos del combo | |
devuelve la posición i del objeto o en el combo |
Puede resultar sorprendente que un combo pueda contener objetos, cuando por lo general contiene cadenas de caracteres. Visualmente, así será. Si un ComboBox contiene un objeto obj, muestra la cadena obj.ToString(). Recordemos que todo objeto tiene un método ToString heredado de la clase object, el cual devuelve una cadena de caracteres «representativa» del objeto. El elemento seleccionado en el menú desplegable C es C.SelectedItem o C.Items(C.SelectedIndex), donde C.SelectedIndex es el número del elemento seleccionado, y este número comienza en cero para el primer elemento.
Al elegir un elemento de la lista desplegable, se produce el evento SelectedIndexChanged, el cual puede utilizarse para recibir una notificación sobre el cambio de selección en el menú desplegable. En la siguiente aplicación, utilizamos este evento para mostrar el elemento que se ha seleccionado en la lista.

Solo presentamos el código relevante de la ventana. En el constructor del formulario, completamos el menú desplegable:
Public Sub New()
' creación del formulario
InitializeComponent()
' llenar el menú desplegable
cmbNombres.Items.AddRange(New String() {"zéro", "un", "deux", "trois", "quatre"})
' seleccionamos el primer elemento de la lista
cmbNombres.SelectedIndex = 0
End Sub
Procesamos el evento SelectedIndexChanged del menú desplegable que indica que se ha seleccionado un nuevo elemento:
Private Sub cmbNombres_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cmbNombres.SelectedIndexChanged
' el elemento seleccionado ha cambiado; lo mostramos
MessageBox.Show("Elément sélectionné : (" & cmbNombres.SelectedItem.ToString & "," & cmbNombres.SelectedIndex & ")", "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
5.4.4. componente ListBox
Nos proponemos construir la siguiente interfaz:
![]() |
Los componentes de esta ventana son los siguientes:
n.º | tipo | nombre | función/propiedades |
0 | Form | Form1 | formulario - BorderStyle=FixedSingle |
1 | TextBox | txtSaisie | campo de entrada |
2 | Botón | btnAjouter | botón que permite agregar el contenido del campo de entrada 1 a la lista 3 |
3 | ListBox | listBox1 | lista 1 |
4 | ListBox | listBox2 | lista 2 |
5 | Botón | btn1TO2 | transfiere los elementos seleccionados de la lista 1 a la lista 2 |
6 | Botón | cmd2T0 | Hace lo contrario |
7 | Botón | btnEffacer1 | vacía la lista 1 |
8 | Botón | btnEffacer2 | borra la lista 2 |
- El usuario escribe texto en el campo 1. Lo agrega a la lista 1 con el botón Ajouter (2). El campo de entrada (1) se vacía y el usuario puede agregar un nuevo elemento.
- Puede transferir elementos de una lista a otra seleccionando el elemento que desea transferir en una de las listas y eligiendo el botón de transferencia correspondiente, 5 o 6. El elemento transferido se agrega al final de la lista de destino y se elimina de la lista de origen.
- Puede hacer doble clic en un elemento de la lista 1. Este elemento se transfiere entonces al cuadro de entrada para su modificación y se elimina de la lista 1.
Los botones están activados o desactivados según las siguientes reglas:
- el botón Ajouter solo está activado si hay texto no vacío en el campo de entrada
- el botón 5 de transferencia de la lista 1 a la lista 2 solo está activado si hay un elemento seleccionado en la lista 1
- el botón 6 de transferencia de la lista 2 a la lista 1 solo está activado si hay un elemento seleccionado en la lista 2
- los botones 7 y 8 para borrar las listas 1 y 2 solo están activados si la lista que se va a borrar contiene elementos.
En las condiciones anteriores, todos los botones deben estar apagados al iniciar la aplicación. Para ello, hay que configurar la propiedad Enabled de los botones en false. Esto se puede hacer en el momento del diseño, lo que generará el código correspondiente en el método InitializeComponent, o bien hacerlo nosotros mismos en el constructor como se muestra a continuación:
Public Sub New()
' creación inicial del formulario
InitializeComponent()
' inicializaciones complementarias
' se desactivan algunos botones
btnAjouter.Enabled = False
btn1TO2.Enabled = False
btn2TO1.Enabled = False
btnEffacer1.Enabled = False
btnEffacer2.Enabled = False
End Sub
El estado del botón Ajouter se controla mediante el contenido del campo de entrada. El evento TextChanged nos permite seguir los cambios en dicho contenido:
' cambio en el campo de entrada de texto
Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtSaisie.TextChanged
' el contenido de txtSaisie ha cambiado
' el botón «Agregar» solo está activo si el campo de entrada no está vacío
btnAjouter.Enabled = txtSaisie.Text.Trim() <> ""
End Sub
El estado de los botones de transferencia depende de si se ha seleccionado o no un elemento en la lista que controlan:
' cambio del elemento seleccionado sin listbox1
Private Sub listBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles listBox1.SelectedIndexChanged
' Se ha seleccionado un elemento
' se activa el botón para transferir de 1 a 2
btn1TO2.Enabled = True
End Sub
' cambio del elemento seleccionado sin el cuadro de lista 2
Private Sub listBox2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles listBox2.SelectedIndexChanged
' Se ha seleccionado un elemento
' se activa el botón de transferencia de 2 a 1
btn2TO1.Enabled = True
End Sub
El código asociado al clic en el botón Ajouter es el siguiente:
' clic en el botón «Agregar»
Private Sub btnAjouter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnAjouter.Click
' se agrega un nuevo elemento a la lista 1
listBox1.Items.Add(txtSaisie.Text.Trim())
' Se borra el dato ingresado
txtSaisie.Text = ""
' La lista 1 no está vacía
btnEffacer1.Enabled = True
' el foco vuelve al campo de entrada
txtSaisie.Focus()
End Sub
Cabe destacar el método Focus, que permite colocar el «foco» en un control del formulario. El código asociado al clic en los botones Effacer:
' clic en el botón Borrar1
Private Sub btnEffacer1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnEffacer1.Click
' se borra la lista 1
listBox1.Items.Clear()
btnEffacer1.Enabled = False
End Sub
' clic en el botón Borrar2
Private Sub btnEffacer2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
' se borra la lista 2
listBox2.Items.Clear()
btnEffacer2.Enabled = False
End Sub
El código para transferir los elementos seleccionados de una lista a otra:
' clic en el botón «btn1to2»
Private Sub btn1TO2_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btn1TO2.Click
' se transfiere el elemento seleccionado de la Lista 1 a la Lista 2
transfert(listBox1, listBox2)
' Botones «Borrar»
btnEffacer2.Enabled = True
btnEffacer1.Enabled = listBox1.Items.Count <> 0
' Botones de transferencia
btn1TO2.Enabled = False
btn2TO1.Enabled = False
End Sub
' clic en el botón btn2to1
Private Sub btn2TO1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btn2TO1.Click
' transferencia del elemento seleccionado de la Lista 2 a la Lista 1
transfert(listBox2, listBox1)
' botones Borrar
btnEffacer1.Enabled = True
btnEffacer2.Enabled = listBox2.Items.Count <> 0
' botones de transferencia
btn1TO2.Enabled = False
btn2TO1.Enabled = False
End Sub
' transferencia
Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
' transferencia del elemento seleccionado de la Lista 1 a la Lista 2
' ¿Un elemento seleccionado?
If l1.SelectedIndex = -1 Then Return
' agregar a l2
l2.Items.Add(l1.SelectedItem)
' eliminación en l1
l1.Items.RemoveAt(l1.SelectedIndex)
End Sub
En primer lugar, creamos un método
Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
que transfiere a la lista l2 el elemento seleccionado de la lista l1. Esto nos permite tener un solo método en lugar de dos para transferir un elemento de listBox1 a listBox2 o de listBox2 a listBox1. Antes de realizar la transferencia, nos aseguramos de que haya un elemento seleccionado en la lista l1:
' ¿Un elemento seleccionado?
If l1.SelectedIndex = -1 Then Return
La propiedad SelectedIndex tiene el valor -1 si actualmente no hay ningún elemento seleccionado. En los procedimientos
Private Sub btnXTOY_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnXTOY.Click
se realiza la transferencia de la lista X a la lista Y y se cambia el estado de ciertos botones para reflejar el nuevo estado de las listas.
5.4.5. casillas de selección CheckBox, botones de opción ButtonRadio
Nos proponemos escribir la siguiente aplicación:
![]() |
Los componentes de la ventana son los siguientes:
n.º | tipo | nombre | función |
1 | RadioButton | radioButton1 radioButton2 radioButton3 | 3 botones de opción |
2 | CheckBox | chechBox1 chechBox2 chechBox3 | 3 casillas de selección |
3 | ListBox | lstValeurs | una lista |
Si creamos los tres botones de opción uno tras otro, por defecto forman parte del mismo grupo. Por lo tanto, cuando uno está marcado, los demás no lo están. El evento que nos interesa para estos seis controles es el evento CheckChanged, que indica que el estado de la casilla de selección o del botón de opción ha cambiado. Este estado se representa en ambos casos mediante la propiedad booleana Check, que, si es verdadera, significa que el control está marcado. Aquí hemos utilizado un solo método para manejar los seis eventos CheckChanged, el método affiche:
' muestra
Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles checkBox1.CheckedChanged, checkBox2.CheckedChanged, checkBox3.CheckedChanged, _
radioButton1.CheckedChanged, radioButton2.CheckedChanged, radioButton3.CheckedChanged
' muestra el estado del botón de opción o de la casilla de selección
' ¿Es una casilla de selección?
If (TypeOf (sender) Is CheckBox) Then
Dim chk As CheckBox = CType(sender, CheckBox)
lstValeurs.Items.Insert(0, chk.Name & "=" & chk.Checked)
End If
' ¿Es un botón de opción?
If (TypeOf (sender) Is RadioButton) Then
Dim rdb As RadioButton = CType(sender, RadioButton)
lstValeurs.Items.Insert(0, rdb.Name & "=" & rdb.Checked)
End If
End Sub
La sintaxis TypeOf (remitente) Is CheckBox permite verificar si el objeto sender es de tipo CheckBox. Esto nos permite luego realizar una conversión de tipo al tipo exacto de sender. El método affiche escribe en la lista lstValeurs el nombre del componente que originó el evento y el valor de su propiedad Checked. Al ejecutarlo, se observa que al hacer clic en un botón de opción se producen dos eventos CheckChanged: uno en el botón que estaba marcado, que pasa a «desmarcado», y otro en el nuevo botón, que pasa a «marcado».
5.4.6. controladores ScrollBar
Existen varios tipos de controladores: el controlador horizontal (hScrollBar), el variador vertical (vScrollBar) y el incrementador (NumericUpDown). | ![]() |
Realicemos la siguiente aplicación:
![]() |
N.º | tipo | nombre | función |
1 | hScrollBar | hScrollBar1 | un variador horizontal |
2 | hScrollBar | hScrollBar2 | un variador horizontal que sigue las variaciones del variador 1 |
3 | TextBox | txtValeur | muestra el valor del variador horizontal ReadOnly=true para impedir cualquier entrada |
4 | NumericUpDown | incrementador | permite fijar el valor del controlador 2 |
- Un control deslizante ScrollBar permite al usuario seleccionar un valor dentro de un rango de valores enteros, representado por la «banda» del control deslizante sobre la que se desplaza un cursor. El valor del control deslizante está disponible en su propiedad Value.
- En un control deslizante horizontal, el extremo izquierdo representa el valor mínimo del rango, el extremo derecho el valor máximo y el cursor el valor actual seleccionado. En un control deslizante vertical, el mínimo está representado por el extremo superior y el máximo por el extremo inferior. Estos valores se representan mediante las propiedades Minimum y Maximum y tienen valores predeterminados de 0 y 100.
- Al hacer clic en los extremos del control deslizante, el valor varía en un incremento (positivo o negativo) según el extremo en el que se haga clic, denominado SmallChange, cuyo valor predeterminado es 1.
- Al hacer clic a ambos lados del cursor, el valor varía en un incremento (positivo o negativo) según el extremo en el que se haya hecho clic, denominado LargeChange, cuyo valor predeterminado es 10.
- Al hacer clic en el extremo superior de un control deslizante vertical, su valor disminuye. Esto puede sorprender al usuario promedio, quien normalmente espera que el valor «aumente». Este problema se resuelve asignando un valor negativo a las propiedades SmallChange y LargeChange
- Estas cinco propiedades (Value, Minimum, Maximum, SmallChange, LargeChange) son accesibles tanto para lectura como para escritura.
- El evento principal del variador es aquel que indica un cambio de valor: el evento Scroll.
Un componente NumericUpDown se encuentra cerca del variador: también cuenta con las propiedades Minimum, Maximum y Value, cuyos valores por defecto son 0, 100 y 0, respectivamente. Sin embargo, en este caso, la propiedad Value se muestra en un campo de entrada que forma parte integral del control. El usuario puede modificar este valor por sí mismo, a menos que se haya establecido la propiedad ReadOnly del control en «verdadero». El valor del incremento se establece mediante la propiedad Increment, cuyo valor predeterminado es 1. El evento principal del componente NumericUpDown es el que notifica un cambio de valor: el evento ValueChanged. El código relevante de nuestra aplicación es el siguiente:
El formulario se formatea al crearse:
' constructor
Public Sub New()
' creación inicial del formulario
InitializeComponent()
' se le asignan al variador 2 las mismas características que al variador 1
hScrollBar2.Minimum = hScrollBar1.Value
hScrollBar2.Minimum = hScrollBar1.Minimum
hScrollBar2.Maximum = hScrollBar1.Maximum
hScrollBar2.LargeChange = hScrollBar1.LargeChange
hScrollBar2.SmallChange = hScrollBar1.SmallChange
' Lo mismo para el incrementador
incrémenteur.Minimum = hScrollBar1.Value
incrémenteur.Minimum = hScrollBar1.Minimum
incrémenteur.Maximum = hScrollBar1.Maximum
incrémenteur.Increment = hScrollBar1.SmallChange
' se le asigna a TextBox el valor del variador 1
txtValeur.Text = "" & hScrollBar1.Value
End Sub
El controlador que supervisa las variaciones de valor del variador 1:
' gestión del variador hscrollbar1
Private Sub hScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
Handles hScrollBar1.Scroll
' cambio de valor del variador 1
' se aplica su valor al variador 2 y al cuadro de texto TxtValeur
hScrollBar2.Value = hScrollBar1.Value
txtValeur.Text = "" & hScrollBar1.Value
End Sub
El controlador que supervisa las variaciones de valor del variador 2:
' gestión del variador hscrollbar2
Private Sub hScrollBar2_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
Handles hScrollBar2.Scroll
' se deshabilita cualquier cambio en el controlador 2
' obligándolo a mantener el valor del variador 1
e.NewValue = hScrollBar1.Value
End Sub
El administrador que supervisa las variaciones del control incrémenteur:
' control del incrementador
Private Sub incrémenteur_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles incrémenteur.ValueChanged
' se fija el valor del variador 2
hScrollBar2.Value = CType(incrémenteur.Value, Integer)
End Sub
5.5. Eventos del ratón
Al dibujar en un contenedor, es importante conocer la posición del ratón para, por ejemplo, mostrar un punto al hacer clic. Los movimientos del ratón generan eventos en el contenedor por el que se desplaza.
![]() | ![]() |
el ratón acaba de ingresar al área del control | |
el ratón acaba de salir del área de control | |
el ratón se mueve dentro del área de control | |
Se presiona el botón izquierdo del ratón | |
Se suelta el botón izquierdo del ratón | |
El usuario suelta un objeto sobre el control | |
El usuario ingresa al área del control arrastrando un objeto | |
el usuario sale del área del control arrastrando un objeto | |
El usuario pasa por encima del área del control al arrastrar un objeto |
A continuación se presenta un programa que permite comprender mejor en qué momentos ocurren los diferentes eventos del ratón:
![]() |
N.º | tipo | nombre | función |
1 | Etiqueta | lblPosition | para mostrar la posición del ratón en el formulario 1, la lista 2 o el botón 3 |
2 | ListBox | lstEvts | para mostrar los eventos del ratón distintos de MouseMove |
3 | Botón | btnEffacer | para borrar el contenido de 2 |
Los manejadores de eventos son los siguientes. Para seguir los movimientos del ratón en los tres controles, solo se escribe un manejador:
' evt form1_mousemove
Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseMove, lstEvts.MouseMove, btnEffacer.MouseMove, lstEvts.MouseMove
' movimiento del ratón: se muestran las coordenadas (X, Y) del mismo
lblPosition.Text = "(" & e.X & "," & e.Y & ")"
End Sub
Es importante saber que cada vez que el ratón entra en el área de un control, su sistema de coordenadas cambia. Su origen (0,0) es la esquina superior izquierda del control en el que se encuentra. Así, al ejecutar el programa, cuando se mueve el ratón del formulario al botón, se observa claramente el cambio de coordenadas. Para ver mejor estos cambios en el área del ratón, se puede utilizar la propiedad Cursor de los controles:

Esta propiedad permite establecer la forma del cursor del ratón cuando este entra en el área del control. Así, en nuestro ejemplo, hemos establecido el cursor en «Default» para el formulario mismo, en «Hand» para la lista 2 y en «No» para el botón 3, como lo muestran las capturas de pantalla a continuación.



En el método [InitializeComponent], el código generado por estas selecciones es el siguiente:
Me.lstEvts.Cursor = System.Windows.Forms.Cursors.Hand
Me.btnEffacer.Cursor = System.Windows.Forms.Cursors.No
Además, para detectar las entradas y salidas del ratón en la lista 2, procesamos los eventos MouseEnter y MouseLeave de esa misma lista:
' evento lstEvts_MouseEnter
Private Sub lstEvts_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles lstEvts.MouseEnter
affiche("MouseEnter sur liste")
End Sub
' evento lstEvts_MouseLeave
Private Sub lstEvts_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles lstEvts.MouseLeave
affiche("MouseLeave sur liste")
End Sub
' muestra
Private Sub affiche(ByVal message As String)
' se muestra el mensaje en la parte superior de la lista de eventos
lstEvts.Items.Insert(0, message)
End Sub

Para procesar los clics en el formulario, procesamos los eventos MouseDown y MouseUp:
' evento Form1_MouseDown
Private Sub Form1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseDown
affiche("MouseDown sur formulaire")
End Sub
' evento Form1_MouseUp
Private Sub Form1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseUp
affiche("MouseUp sur formulaire")
End Sub

Por último, el código del controlador de clic en el botón Effacer:
' evento btnEffacer_Click
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnEffacer.Click
' borra la lista de eventos
lstEvts.Items.Clear()
End Sub
5.6. Crear una ventana con menú
Ahora veamos cómo crear una ventana con menú. Vamos a crear la siguiente ventana:
![]() |
El control 1 es un TextBox de solo lectura (ReadOnly=true) y se llama txtStatut. La estructura del menú es la siguiente:
![]() | ![]() |
Las opciones de menú son controles como los demás componentes visuales y cuentan con propiedades y eventos. Por ejemplo, la tabla de propiedades de la opción de menú A1:

En nuestro ejemplo se utilizan dos propiedades:
el nombre del control de menú | |
el texto de la opción del menú |
Las propiedades de las diferentes opciones de menú de nuestro ejemplo son las siguientes:
Nombre | Texto |
mnuA | Opciones A |
mnuA1 | A1 |
mnuA2 | A2 |
mnuA3 | A3 |
mnuB | opciones B |
mnuB1 | B1 |
mnuSep1 | - (separador) |
mnuB2 | B2 |
mnuB3 | B3 |
mnuB31 | B31 |
mnuB32 | B32 |
Para crear un menú, se selecciona el componente «MainMenu» en la barra «ToolBox»:

De esta manera, se genera un menú vacío que se inserta en el formulario con casillas en blanco tituladas «Type Here». Solo hay que indicar allí las diferentes opciones del menú:

Para insertar un separador entre dos opciones, como se muestra arriba entre las opciones B1 y B2, colóquese en el lugar donde desea insertar el separador en el menú, haga clic con el botón derecho y seleccione la opción Insertar separador:

Si se inicia la aplicación con Ctrl+F5, se obtiene un formulario con un menú que, por el momento, no hace nada. Las opciones del menú se tratan como componentes: tienen propiedades y eventos. En [fenêtre de code], seleccione el componente mnuA1 y luego seleccione los eventos asociados:

Si en el ejemplo anterior se genera el evento Click, VS.NET genera automáticamente el siguiente procedimiento:
Private Sub mnuA1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuA.Click
....
End Sub
Podríamos proceder de esta manera para todas las opciones del menú. Aquí, se puede utilizar el mismo procedimiento para todas las opciones. Por lo tanto, renombramos el procedimiento anterior como affiche y declaramos los eventos que maneja:
Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mnuA1.Click, mnuA2.Click, mnuB1.Click, mnuB2.Click, mnuB31.Click, mnuB32.Click
' muestra en el TextBox el nombre del submenú seleccionado
txtStatut.Text = (CType(sender, MenuItem)).Text
End Sub
En este método, nos limitamos a mostrar la propiedad Text de la opción de menú en el origen del evento. El origen del evento sender es de tipo object. Las opciones de menú son de tipo MenuItem, por lo que aquí nos vemos obligados a realizar una conversión de tipo de object a MenuItem. Ejecute la aplicación y seleccione la opción A1 para obtener el siguiente mensaje:

El código relevante de esta aplicación, además del del método affiche, es el de la construcción del menú en el generador de formularios (InitializeComponent):
Private Sub InitializeComponent()
Me.mainMenu1 = New System.Windows.Forms.MainMenu
Me.mnuA = New System.Windows.Forms.MenuItem
Me.mnuA1 = New System.Windows.Forms.MenuItem
Me.mnuA2 = New System.Windows.Forms.MenuItem
Me.mnuA3 = New System.Windows.Forms.MenuItem
Me.mnuB = New System.Windows.Forms.MenuItem
Me.mnuB1 = New System.Windows.Forms.MenuItem
Me.mnuB2 = New System.Windows.Forms.MenuItem
Me.mnuB3 = New System.Windows.Forms.MenuItem
Me.mnuB31 = New System.Windows.Forms.MenuItem
Me.mnuB32 = New System.Windows.Forms.MenuItem
Me.txtStatut = New System.Windows.Forms.TextBox
Me.mnuSep1 = New System.Windows.Forms.MenuItem
Me.SuspendLayout()
'
' mainMenu1
'
Me.mainMenu1.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA, Me.mnuB})
'
' mnuA
'
Me.mnuA.Index = 0
Me.mnuA.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA1, Me.mnuA2, Me.mnuA3})
Me.mnuA.Text = "Options A"
'
' mnuA1
'
Me.mnuA1.Index = 0
Me.mnuA1.Text = "A1"
'
' mnuA2
'
Me.mnuA2.Index = 1
Me.mnuA2.Text = "A2"
'
' mnuA3
'
Me.mnuA3.Index = 2
Me.mnuA3.Text = "A3"
'
' mnuB
'
Me.mnuB.Index = 1
Me.mnuB.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB1, Me.mnuSep1, Me.mnuB2, Me.mnuB3})
Me.mnuB.Text = "Options B"
'
' mnuB1
'
Me.mnuB1.Index = 0
Me.mnuB1.Text = "B1"
'
' mnuB2
'
Me.mnuB2.Index = 2
Me.mnuB2.Text = "B2"
'
' mnuB3
'
Me.mnuB3.Index = 3
Me.mnuB3.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB31, Me.mnuB32})
Me.mnuB3.Text = "B3"
'
' mnuB31
'
Me.mnuB31.Index = 0
Me.mnuB31.Text = "B31"
'
' mnuB32
'
Me.mnuB32.Index = 1
Me.mnuB32.Text = "B32"
'
' txtStatut
'
Me.txtStatut.Location = New System.Drawing.Point(8, 8)
Me.txtStatut.Name = "txtStatut"
Me.txtStatut.ReadOnly = True
Me.txtStatut.Size = New System.Drawing.Size(112, 20)
Me.txtStatut.TabIndex = 0
Me.txtStatut.Text = ""
'
' mnuSep1
'
Me.mnuSep1.Index = 1
Me.mnuSep1.Text = "-"
'
' Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(136, 42)
Me.Controls.Add(txtStatut)
Me.Menu = Me.mainMenu1
Me.Name = "Form1"
Me.Text = "Menus"
Me.ResumeLayout(False)
End Sub
Cabe destacar la instrucción que vincula el menú al formulario:
Me.Menu = Me.mainMenu1
5.7. Componentes no visuales
Ahora nos enfocaremos en una serie de componentes no visuales: se utilizan durante el diseño, pero no se ven durante la ejecución.
5.7.1. Cuadros de diálogo OpenFileDialog y SaveFileDialog
Vamos a crear la siguiente aplicación:
![]() |
Los controles son los siguientes:
N.º | tipo | nombre | función |
1 | TextBox multilíneas | txtTexte | texto escrito por el usuario o cargado desde un archivo |
2 | Botón | btnSauvegarder | permite guardar el texto de 1 en un archivo de texto |
3 | Botón | btnCharger | permite cargar el contenido de un archivo de texto en 1 |
4 | Botón | btnEffacer | borra el contenido de 1 |
Se utilizan dos controles no visuales:

Cuando se seleccionan en el «ToolBox» y se colocan en el formulario, se ubican en un área separada al final del formulario. Los componentes «Dialog» se seleccionan en el «ToolBox»:

El código del botón Effacer es sencillo:
Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnEffacer.Click
' se borra el campo de entrada
txtTexte.Text = ""
End Sub
La clase SaveFileDialog se define de la siguiente manera:

Deriva de varios niveles de clase. De sus numerosas propiedades y métodos, destacaremos los siguientes:
los tipos de archivos que aparecen en la lista desplegable de tipos de archivos del cuadro de diálogo | |
el número del tipo de archivo que aparece por defecto en la lista anterior. Comienza en 0. | |
la carpeta que se muestra inicialmente para guardar el archivo | |
el nombre del archivo de copia de seguridad indicado por el usuario | |
método que muestra el cuadro de diálogo de copia de seguridad. Devuelve un resultado de tipo DialogResult. |
El método ShowDialog muestra un cuadro de diálogo similar al siguiente:
![]() |
lista desplegable generada a partir de la propiedad Filter. El tipo de archivo propuesto por defecto se establece mediante FilterIndex | |
carpeta actual, determinada por InitialDirectory si se ha completado esta propiedad | |
el nombre del archivo seleccionado o ingresado directamente por el usuario. Estará disponible en la propiedad FileName | |
Botones Guardar/Cancelar. Si se utiliza el botón Enregistrer, la función ShowDialog devuelve el resultado DialogResult.OK |
El procedimiento de guardado se puede escribir así:
Private Sub btnSauvegarder_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnSauvegarder.Click
' se guarda el cuadro de entrada en un archivo de texto
' se configura el cuadro de diálogo savefileDialog1
saveFileDialog1.InitialDirectory = Application.ExecutablePath
saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
saveFileDialog1.FilterIndex = 0
' se muestra el cuadro de diálogo y se recupera su resultado
If saveFileDialog1.ShowDialog() = DialogResult.OK Then
' se recupera el nombre del archivo
Dim nomFichier As String = saveFileDialog1.FileName
Dim fichier As StreamWriter = Nothing
Try
' se abre el archivo en modo de escritura
fichier = New StreamWriter(nomFichier)
' se escribe el texto aquí
fichier.Write(txtTexte.Text)
Catch ex As Exception
' problema
MessageBox.Show("Problème à l'écriture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
Finally
' se cierra el archivo
Try
fichier.Close()
Catch
End Try
End Try
End If
End Sub
- Se vincula el archivo inicial al archivo que contiene el ejecutable de la aplicación:
- Se definen los tipos de archivos que se mostrarán
Obsérvese la sintaxis de los filtros: filtro1|filtro2|...|filtro, donde filtro = Texto|plantilla de archivo. Aquí el usuario podrá elegir entre archivos *.txt y *.*.
- Se establece el tipo de archivo que se mostrará al inicio
Aquí, los archivos de tipo *.txt serán los primeros en mostrarse al usuario.
- Se muestra el cuadro de diálogo y se recupera su resultado
If saveFileDialog1.ShowDialog() = DialogResult.OK Then
- Mientras se muestra el cuadro de diálogo, el usuario ya no tiene acceso al formulario principal (cuadro de diálogo denominado «modal»). El usuario establece el nombre del archivo que desea guardar y sale del cuadro de diálogo, ya sea presionando el botón «Guardar», el botón «Cancelar» o cerrando el cuadro de diálogo. El resultado del método ShowDialog es DialogResult.OK únicamente si el usuario ha utilizado el botón Enregistrer para salir del cuadro de diálogo.
- Una vez hecho esto, el nombre del archivo que se creará se encuentra ahora en la propiedad FileName del objeto saveFileDialog1. A continuación, se vuelve al proceso clásico de creación de un archivo de texto. En él se escribe el contenido de TextBox: txtTexte.Text, al tiempo que se gestionan las excepciones que puedan surgir.
La clase OpenFileDialog es muy similar a la clase SaveFileDialog y deriva de la misma línea de clases. De estas propiedades y métodos, destacaremos los siguientes:
los tipos de archivos que aparecen en la lista desplegable de tipos de archivos del cuadro de diálogo | |
el número del tipo de archivo que aparece por defecto en la lista anterior. Comienza en 0. | |
la carpeta que se muestra inicialmente para buscar el archivo que se va a abrir | |
el nombre del archivo que se va a abrir, indicado por el usuario | |
método que muestra el cuadro de diálogo para guardar. Devuelve un resultado de tipo DialogResult. |
El método ShowDialog muestra un cuadro de diálogo similar al siguiente:
![]() |
lista desplegable generada a partir de la propiedad Filter. El tipo de archivo propuesto por defecto lo establece FilterIndex | |
carpeta actual, determinada por InitialDirectory si se ha completado esta propiedad | |
el nombre del archivo elegido o ingresado directamente por el usuario. Estará disponible en la propiedad FileName | |
Botones Abrir/Cancelar. Si se utiliza el botón Ouvrir, la función ShowDialog devuelve el resultado DialogResult.OK |
El procedimiento de apertura se puede escribir así:
Private Sub btnCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnCharger.Click
' se carga un archivo de texto en el cuadro de entrada
' se configura el cuadro de diálogo openfileDialog1
openFileDialog1.InitialDirectory = Application.ExecutablePath
openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
openFileDialog1.FilterIndex = 0
' se muestra el cuadro de diálogo y se recupera su resultado
If openFileDialog1.ShowDialog() = DialogResult.OK Then
' se recupera el nombre del archivo
Dim nomFichier As String = openFileDialog1.FileName
Dim fichier As StreamReader = Nothing
Try
' se abre el archivo en modo de lectura
fichier = New StreamReader(nomFichier)
' se lee todo el archivo y se guarda en el TextBox
txtTexte.Text = fichier.ReadToEnd()
Catch ex As Exception
' problema
MessageBox.Show("Problème à la lecture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
Finally
' se cierra el archivo
Try
fichier.Close()
Catch
End Try
End Try
End If
End Sub
- Se establece la carpeta inicial como la carpeta que contiene el ejecutable de la aplicación:
- Se definen los tipos de archivos que se van a presentar
- Se establece el tipo de archivo que se mostrará al inicio
En este caso, los archivos de tipo *.txt serán los primeros en mostrarse al usuario.
- Se muestra el cuadro de diálogo y se recupera su resultado
If openFileDialog1.ShowDialog() = DialogResult.OK Then
Mientras se muestra el cuadro de diálogo, el usuario ya no tiene acceso al formulario principal (cuadro de diálogo denominado «modal»). El usuario establece el nombre del archivo que desea abrir y sale del cuadro de diálogo, ya sea presionando el botón Abrir, el botón Cancelar o cerrando el cuadro de diálogo. El resultado del método ShowDialog es DialogResult.OK únicamente si el usuario ha utilizado el botón Ouvrir para salir del cuadro de diálogo.
- Una vez hecho esto, el nombre del archivo que se va a crear se encuentra ahora en la propiedad FileName del objeto openFileDialog1. A partir de ahí, se vuelve a la lectura convencional de un archivo de texto. Cabe destacar el método que permite leer la totalidad de un archivo:
- el contenido del archivo se coloca en TextBox y txtTexte. Se manejan las excepciones que puedan ocurrir.
5.7.2. Cuadros de diálogo FontColor y ColorDialog
Continuamos con el ejemplo anterior presentando dos botones nuevos:
![]() |
N.º | tipo | nombre | función |
6 | Botón | btnCouleur | para establecer el color de los caracteres de TextBox |
7 | Botón | btnPolice | para establecer la fuente de TextBox |
Incorporamos al formulario un control ColorDialog y un control FontDialog:

Las clases FontDialog y ColorDialog cuentan con un método ShowDialog análogo al método ShowDialog de las clases OpenFileDialog y SaveFileDialog. El método ShowDialog de la clase ColorDialog permite seleccionar un color:

Si el usuario sale del cuadro de diálogo con el botón OK, el resultado del método ShowDialog es DialogResult.OK y el color seleccionado se encuentra en la propiedad Color del objeto ColorDialog utilizado. El método ShowDialog de la clase FontDialog permite seleccionar una fuente:

Si el usuario sale del cuadro de diálogo con el botón OK, el resultado del método ShowDialog es DialogResult.OK y la fuente seleccionada se encuentra en la propiedad Font del objeto FontDialog utilizado. Contamos con los elementos para manejar los clics en los botones Couleur y Police:
Private Sub btnCouleur_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnCouleur.Click
' selección de un color de texto
If colorDialog1.ShowDialog() = DialogResult.OK Then
' se cambia la propiedad «forecolor» de TextBox
txtTexte.ForeColor = colorDialog1.Color
End If
End Sub
Private Sub btnPolice_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnPolice.Click
' selección de una fuente
If fontDialog1.ShowDialog() = DialogResult.OK Then
' se cambia la propiedad «font» de TextBox
txtTexte.Font = fontDialog1.Font
End If
End Sub
5.7.3. Temporizador
Aquí nos proponemos escribir la siguiente aplicación:
![]() |
N.º | Tipo | Nombre | Función |
1 | TextBox, ReadOnly=true | txtChrono | muestra un cronómetro |
2 | Botón | btnArretMarche | botón de inicio/parada del cronómetro |
3 | Temporizador | timer1 | componente que emite aquí un evento cada segundo |
El cronómetro en funcionamiento:

El cronómetro detenido:

Para cambiar cada segundo el contenido de TextBox y txtChrono, necesitamos un componente que genere un evento cada segundo, evento que podremos interceptar para actualizar la visualización del cronómetro. Ese componente es el Timer:

Una vez instalado este componente en el formulario (en la sección de componentes no visuales), se crea un objeto de tipo Timer en el constructor del formulario. La clase System.Windows.Forms.Timer se define de la siguiente manera:

De sus propiedades, solo destacaremos las siguientes:
número de milisegundos tras los cuales se emite un evento Tick. | |
el evento se produce al cabo de Interval milisegundos | |
activa (true) o desactiva (false) el temporizador |
En nuestro ejemplo, el temporizador se llama timer1 y timer1.Interval está configurado en 1000 ms (1 s). Por lo tanto, el evento Tick se producirá cada segundo. Al hacer clic en el botón «On/Off», se ejecuta el siguiente procedimiento:
Private Sub btnArretMarche_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnArretMarche.Click
' ¿Apagar o encender?
If btnArretMarche.Text = "Marche" Then
' se anota la hora de inicio
début = DateTime.Now
' se muestra
txtChrono.Text = "00:00:00"
' se inicia el temporizador
timer1.Enabled = True
' se cambia el texto del botón
btnArretMarche.Text = "Arrêt"
' fin
Return
End If '
If btnArretMarche.Text = "Arrêt" Then
' se detiene el temporizador
timer1.Enabled = False
' se cambia el texto del botón
btnArretMarche.Text = "Marche"
' fin
Return
End If
End Sub
Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles timer1.Tick
' ha transcurrido un segundo
Dim maintenant As DateTime = DateTime.Now
Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
End Sub
El texto del botón «Detener/Iniciar» es «Detener» o «Iniciar». Por lo tanto, es necesario realizar una comprobación de este texto para determinar qué acción realizar.
- En el caso de «Encendido», se registra la hora de inicio en una variable global del objeto formulario, se inicia el temporizador (Enabled=true) y el texto del botón cambia a «Apagado».
- En el caso de «Parar», se detiene el temporizador (Enabled=false) y se cambia el texto del botón a «Iniciar».
Public Class Timer1
Inherits System.Windows.Forms.Form
Private WithEvents timer1 As System.Windows.Forms.Timer
Private WithEvents btnArretMarche As System.Windows.Forms.Button
Private components As System.ComponentModel.IContainer
Private WithEvents txtChrono As System.Windows.Forms.TextBox
Private WithEvents label1 As System.Windows.Forms.Label
' variables de instancia
Private début As DateTime
El atributo début mencionado anteriormente está disponible en todos los métodos de la clase. Nos queda por manejar el evento Tick en el objeto timer1, evento que ocurre cada segundo:
Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles timer1.Tick
' ha transcurrido un segundo
Dim maintenant As DateTime = DateTime.Now
Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
End Sub
Calculamos el tiempo transcurrido desde el momento en que se inició el cronómetro. Se obtiene un objeto de tipo TimeSpan que representa un lapso de tiempo. Este debe mostrarse en el cronómetro en el formato hh:mm:ss. Para ello, utilizamos las propiedades Hours, Minutes, Seconds del objeto TimeSPan, que representan, respectivamente, las horas, los minutos y los segundos de la duración que mostramos en el formato ToString("d2") para que se muestre con dos dígitos.
5.8. El ejemplo IMPOTS
Retomamos la aplicación IMPOTS que ya hemos tratado dos veces. Ahora le agregamos una interfaz gráfica:
![]() |
Los controles son los siguientes
n.º | tipo | nombre | función |
RadioButton | rdOui | marcado si está casado | |
RadioButton | rdNon | marcar si no está casado | |
NumericUpDown | incEnfants | número de hijos del contribuyente Mínimo=0, Máximo=20, Incremento=1 | |
TextBox | txtSalaire | salario anual del contribuyente en F | |
TextBox | txtImpots | monto del impuesto a pagar ReadOnly=true | |
Botón | btnCalculer | inicia el cálculo del impuesto | |
Botón | btnEffacer | restablece el formulario a su estado inicial al cargarlo | |
Botón | btnQuitter | para salir de la aplicación |
Reglas de funcionamiento
- El botón «Calcular» permanece desactivado mientras no haya nada en el campo del salario
- si, al iniciar el cálculo, resulta que el salario es incorrecto, se muestra un mensaje de error:

A continuación se presenta el programa. Utiliza la clase impot creada en el capítulo sobre clases. Una parte del código generado automáticamente por VS.NET no se ha reproducido aquí.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
' opciones
Option Explicit On
Option Strict On
' espacios de nombres
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
' clase de formulario
Public Class frmImpots
Inherits System.Windows.Forms.Form
Private WithEvents label1 As System.Windows.Forms.Label
Private WithEvents rdOui As System.Windows.Forms.RadioButton
Private WithEvents rdNon As System.Windows.Forms.RadioButton
Private WithEvents label2 As System.Windows.Forms.Label
Private WithEvents txtSalaire As System.Windows.Forms.TextBox
Private WithEvents label3 As System.Windows.Forms.Label
Private WithEvents label4 As System.Windows.Forms.Label
Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
Private WithEvents btnCalculer As System.Windows.Forms.Button
Private WithEvents btnEffacer As System.Windows.Forms.Button
Private WithEvents btnQuitter As System.Windows.Forms.Button
Private WithEvents txtImpots As System.Windows.Forms.TextBox
Private components As System.ComponentModel.Container = Nothing
Private WithEvents incEnfants As System.Windows.Forms.NumericUpDown
' tablas de datos necesarias para el cálculo del impuesto
Private limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
Private coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.55D, 0.5D, 0.6D, 0.65D}
Private coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}
' objeto de impuesto
Private objImpôt As impot = Nothing
Public Sub New()
InitializeComponent()
' inicialización del formulario
btnEffacer_Click(Nothing, Nothing)
btnCalculer.Enabled = False
' creación de un objeto de impuesto
Try
objImpôt = New impot(limites, coeffR, coeffN)
Catch ex As Exception
MessageBox.Show("Impossible de créer l'objet impôt (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
' se desactiva el campo de entrada del salario
txtSalaire.Enabled = False
End Try 'try-catch
End Sub
Protected Overloads Sub Dispose(ByVal disposing As Boolean)
....
End Sub
Private Sub InitializeComponent()
Me.btnQuitter = New System.Windows.Forms.Button
Me.groupBox1 = New System.Windows.Forms.GroupBox
Me.btnEffacer = New System.Windows.Forms.Button
Me.btnCalculer = New System.Windows.Forms.Button
Me.txtSalaire = New System.Windows.Forms.TextBox
Me.label1 = New System.Windows.Forms.Label
Me.label2 = New System.Windows.Forms.Label
Me.label3 = New System.Windows.Forms.Label
Me.rdNon = New System.Windows.Forms.RadioButton
Me.txtImpots = New System.Windows.Forms.TextBox
Me.label4 = New System.Windows.Forms.Label
Me.rdOui = New System.Windows.Forms.RadioButton
Me.incEnfants = New System.Windows.Forms.NumericUpDown
Me.groupBox1.SuspendLayout()
CType(Me.incEnfants, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
....
End Sub 'InitializeComponent
Public Shared Sub Main()
Application.Run(New frmImpots)
End Sub 'Main
Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnEffacer.Click
' borrado del formulario
incEnfants.Value = 0
txtSalaire.Text = ""
txtImpots.Text = ""
rdNon.Checked = True
End Sub 'btnEffacer_Click
Private Sub txtSalaire_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtSalaire.TextChanged
' Estado del botón «Calcular»
btnCalculer.Enabled = txtSalaire.Text.Trim() <> ""
End Sub 'txtSalaire_TextChanged
Private Sub btnQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnQuitter.Click
' fin de la aplicación
Application.Exit()
End Sub 'btnQuitter_Click
Private Sub btnCalculer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnCalculer.Click
' ¿Es correcto el salario?
Dim intSalaire As Integer = 0
Try
' Recuperación del salario
intSalaire = Integer.Parse(txtSalaire.Text)
' debe ser >=0
If intSalaire < 0 Then
Throw New Exception("")
End If
Catch ex As Exception
' mensaje de error
MessageBox.Show(Me, "Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error)
' foco en campo erróneo
txtSalaire.Focus()
' Selección del texto del campo de entrada
txtSalaire.SelectAll()
' regreso a la interfaz visual
Return
End Try 'try-catch
' el salario es correcto: se calcula el impuesto
txtImpots.Text = "" & CLng(objImpôt.calculer(rdOui.Checked, CInt(incEnfants.Value), intSalaire))
End Sub 'btnCalculer_Click
End Class
Aquí utilizamos el ensamblado impots.dll, resultado de la compilación de la clase impots del capítulo 2. Recordemos que este ensamblado se puede generar en modo consola mediante el comando
Este comando genera el archivo impots.dll, denominado ensamblado. Este ensamblado se puede utilizar posteriormente en diferentes proyectos. Aquí, en nuestro proyecto bajo VS.NET, utilizamos la ventana de propiedades del proyecto:

Para agregar una referencia (un ensamblaje), hacemos clic con el botón derecho en el grupo clave «Referencias» que se muestra arriba, seleccionamos la opción [Ajouter une référence] y designamos el ensamblaje [impots.dll] que nos hemos asegurado de colocar en la carpeta del proyecto:
dos>dir
01/03/2004 14:39 9 250 gui_impots.vb
01/03/2004 14:37 4 096 impots.dll
01/03/2004 14:41 12 288 gui_impots.exe
Una vez incluido el ensamblaje [impots.dll] en el proyecto, la clase [impots] pasa a ser reconocida por el proyecto. Antes no lo era. Otro método consiste en incluir el código fuente impots.vb en el proyecto. Para ello, en la ventana de propiedades del proyecto, haz clic con el botón derecho sobre el proyecto, selecciona la opción [Ajouter/Ajouter un élément existant] y selecciona el archivo impots.vb.















































