Skip to content

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

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm1.vb

dos>frm1

La ejecución muestra la siguiente ventana:

Image

Una interfaz gráfica suele derivarse de la clase base System.Windows.Forms.Form:

Public Class Form1
    Inherits 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:

Public Event Click As EventHandler

donde EventHandler es una función «delegada» con la siguiente firma:

Public Delegate Sub EventHandler(ByVal sender As Object,ByVal e As EventArgs)

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:

Image

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
   AbortRetryIgnore 
  OK 
    OKCancel 
    RetryCancel 
    YesNo 
    YesNoCancel 

El parámetro icon puede tomar valores de entre las siguientes constantes:

Asterisk
Error
ídem Stop
Exclamation
ídem Advertencia
Hand
Information
Lo mismo que Asterisk
None
Question
Stop
ídem Mano
Warning

 

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

  1. Inicie VS.NET y seleccione la opción Fichier/Nouveau/Projet

Image

  1. Ingrese las características de su proyecto
    1. Seleccione el tipo de proyecto que desea crear; en este caso, un proyecto VB.NET (1)
    2. selecciona el tipo de aplicación que deseas crear; en este caso, una aplicación de Windows (2)
    3. Indica en qué carpeta quieres colocar la subcarpeta del proyecto (3)
    4. Indica el nombre del proyecto (4). Este será también el nombre de la carpeta que contendrá los archivos del proyecto
    5. El nombre de esta carpeta se muestra en (5)
  1. 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:

Image

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

Image

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):

Image

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:

Image

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:

Image

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:

Image

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:

Image

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:

Public Class Form1
    Inherits 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]:

Image

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]:

Image

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:

Imports System.Windows.Forms

y luego recompilemos:

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

Esta vez sí funciona. Ejecutemos:

dos>dir
14/03/2004  11:53               514 Form1.vb
14/03/2004  12:15             3 584 Form1.exe

dos>form1

Image

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:

dos>vbc /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /m:form2 form2.vb

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:

Image

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:

Image

La ventana de propiedades aparecerá entonces a la derecha:

Image

Cabe destacar algunas de estas propiedades:

BackColor
para establecer el color de fondo de la ventana
ForeColor
para establecer el color de los dibujos o del texto en la ventana
Menu
para asociar un menú a la ventana
Text
para asignar un título a la ventana
FormBorderStyle
para establecer el tipo de ventana
Font
para establecer el tipo de letra del texto en la ventana
Name
para establecer el nombre de la ventana

Aquí configuramos las propiedades Text y Name:

Text
Campos de entrada y botones - 1
Name
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:
  1. seleccione los distintos componentes que desea formatear juntos (manteniendo presionada la tecla Ctrl)
  2. seleccione el tipo de formato deseado
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:
  • la etiqueta 1 (Label)
Seleccione el componente para acceder a
su ventana de propiedades. En ella,
modifica las siguientes propiedades:
nombre: lblSaisie, texto: Campo de entrada
  • el campo de entrada 2 (TextBox)
Seleccione el componente para abrir su
ventana de propiedades. En ella, modifica
las siguientes propiedades:
nombre: txtSaisie, texto: no poner nada
  • el botón 3 (Button):
nombre: cmdAfficher, texto: Mostrar
  • la ventana misma: nombre: frmSaisies&Botones,
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:

Image

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

Image

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:

sender
el objeto que origina el evento (en este caso, el botón)
e
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:

Image

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:
Addhandler E, AddressOf P1
Addhandler E, AddressOf P2
...
Addhandler E, AddressOf Pn

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
Closing
El formulario se está cerrando
Closed
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:

Image

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
ScrollBars=( None, Horizontal, Vertical, Both)
para indicar si el control debe tener barras de desplazamiento (Horizontal, Vertical, Both) o no (None)
AcceptReturn=(True, False)
si es igual a true, la tecla Enter pasará a la línea siguiente
AcceptTab=(True, False)
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
DropDown
lista desplegable con campo de edición
DropDownList
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:

Public ReadOnly Property Items As ComboBox.ObjectCollection

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
C.Items(i)
elemento i del combo
C.Add(object o)
agrega el objeto o como último elemento del combo
C.AddRange(object() objets)
agrega una tabla de objetos al final del combo
C.Insert(int i, object o)
agrega el objeto o en la posición i del combo
C.RemoveAt(int i)
elimina el elemento i del combo
C.Remove(object o)
elimina el objeto o del combo
C.Clear()
elimina todos los elementos del combo
C.IndexOf(object o)
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.

Image

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
MouseLeave
el ratón acaba de salir del área de control
MouseMove
el ratón se mueve dentro del área de control
MouseDown
Se presiona el botón izquierdo del ratón
MouseUp
Se suelta el botón izquierdo del ratón
DragDrop
El usuario suelta un objeto sobre el control
DragEnter
El usuario ingresa al área del control arrastrando un objeto
DragLeave
el usuario sale del área del control arrastrando un objeto
DragOver
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:

Image

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.

Image

Image

Image

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

Image

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

Image

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:

Image

En nuestro ejemplo se utilizan dos propiedades:

 
el nombre del control de menú
Text
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»:

Image

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ú:

Image

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:

Image

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:

Image

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:

Image

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:

Image

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»:

Image

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:

Image

Deriva de varios niveles de clase. De sus numerosas propiedades y métodos, destacaremos los siguientes:

string Filter
los tipos de archivos que aparecen en la lista desplegable de tipos de archivos del cuadro de diálogo
int FilterIndex
el número del tipo de archivo que aparece por defecto en la lista anterior. Comienza en 0.
String
InitialDirectory
la carpeta que se muestra inicialmente para guardar el archivo
string FileName
el nombre del archivo de copia de seguridad indicado por el usuario
DialogResult.ShowDialog()
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:

1
lista desplegable generada a partir de la propiedad Filter. El tipo de archivo propuesto por defecto se establece mediante FilterIndex
2
carpeta actual, determinada por InitialDirectory si se ha completado esta propiedad
3
el nombre del archivo seleccionado o ingresado directamente por el usuario. Estará disponible en la propiedad FileName
4
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:
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • Se definen los tipos de archivos que se mostrarán
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"

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
        saveFileDialog1.FilterIndex = 0

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:

string Filter
los tipos de archivos que aparecen en la lista desplegable de tipos de archivos del cuadro de diálogo
int FilterIndex
el número del tipo de archivo que aparece por defecto en la lista anterior. Comienza en 0.
string InitialDirectory
la carpeta que se muestra inicialmente para buscar el archivo que se va a abrir
string FileName
el nombre del archivo que se va a abrir, indicado por el usuario
DialogResult.ShowDialog()
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:

1
lista desplegable generada a partir de la propiedad Filter. El tipo de archivo propuesto por defecto lo establece FilterIndex
2
carpeta actual, determinada por InitialDirectory si se ha completado esta propiedad
3
el nombre del archivo elegido o ingresado directamente por el usuario. Estará disponible en la propiedad FileName
4
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:
            saveFileDialog1.InitialDirectory=Application.ExecutablePath
  • Se definen los tipos de archivos que se van a presentar
            saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
  • Se establece el tipo de archivo que se mostrará al inicio
            saveFileDialog1.FilterIndex = 0

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:
                    txtTexte.Text=fichier.ReadToEnd
  • 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:

Image

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:

Image

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:

Image

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:

Image

El cronómetro detenido:

Image

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:

Image

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:

Image

De sus propiedades, solo destacaremos las siguientes:

Interval
número de milisegundos tras los cuales se emite un evento Tick.
Tick
el evento se produce al cabo de Interval milisegundos
Enabled
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
1
RadioButton
rdOui
marcado si está casado
2
RadioButton
rdNon
marcar si no está casado
3
NumericUpDown
incEnfants
número de hijos del contribuyente
Mínimo=0, Máximo=20, Incremento=1
4
TextBox
txtSalaire
salario anual del contribuyente en F
5
TextBox
txtImpots
monto del impuesto a pagar
ReadOnly=true
6
Botón
btnCalculer
inicia el cálculo del impuesto
7
Botón
btnEffacer
restablece el formulario a su estado inicial al cargarlo
8
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:

Image

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

dos>vbc /t:library impots.vb

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:

Image

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.