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. A continuación, 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 utilizada es la versión en inglés.

5.1. Fundamentos de las interfaces gráficas

5.1.1. Una ventana sencilla

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

    ' 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 de cierre, maximizar/minimizar, tamaño ajustable, etc., y gestiona los eventos de estos objetos gráficos. Aquí especializamos la clase base asignándole un título y su anchura (300) y altura (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; a continuación, la aplicación se pone a la escucha de los eventos que se producen en el formulario (clics, movimientos del ratón, etc.) y ejecuta aquellos que el formulario gestiona. En este caso, nuestro formulario no gestiona ningún evento más allá de los que gestiona 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

Añadamos ahora 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
        ' Añadir 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 ha hecho 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 añadido 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
        ' Añadir 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 fijan 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 controlador 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 EventHandler anterior:


    ' gestor de eventos
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' se ha hecho clic en el botón - se notifica
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

Nos limitamos a mostrar 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 va a mostrar
caption
el título de la ventana
botones
los botones presentes en la ventana
icono
el icono presente en la ventana

El parámetro buttons puede tomar sus valores entre las siguientes constantes:

constante
botones
   AbortRetryIgnore
  OK
    OKCancel
    RetryCancel
    YesNo
    YesNoCancel

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

Asterisk
Error
ídem Stop
Exclamation
ídem Advertencia
Hand
Information
ídem Asterisk
None
Question
Stop
ídem Hand
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 ha pulsado 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 los creamos ahora 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. Introduzca las características de su proyecto
    1. seleccione el tipo de proyecto que desea crear, en este caso un proyecto VB.NET (1)
    2. seleccione el tipo de aplicación que desea crear, en este caso una aplicación de Windows (2)
    3. Indique en qué carpeta desea colocar la subcarpeta del proyecto (3)
    4. Indique 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 fuente asociado al formulario creado por VS.NET. Volveremos sobre ello más adelante.

5.2.2. Las ventanas de la interfaz de VS.NET

La interfaz de VS.NET muestra ahora 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 (toolbox 2) y soltarlos en la superficie de la ventana (1), podemos construir una interfaz gráfica. Si pasamos el ratón por encima de la «toolbox», esta se amplía y muestra una serie de controles:

Image

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

Image

En un primer momento, apenas utilizaremos esta ventana. Muestra todos los archivos que componen 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 del ratón 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] designa la ventana de diseño y Form1.vb la ventana de código. Basta con hacer clic en una de las pestañas para pasar de una ventana a 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

En él 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, 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 formularios de Windows»

    Public Sub New()
        MyBase.New()

        'Esta llamada es requerida por el Diseñador de Windows Form.
        InitializeComponent()

        'Añada cualquier inicialización después de la llamada InitializeComponent()

    End Sub

    'El método sustituido Dispose del formulario para limpiar 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 Form
    Private components As System.ComponentModel.IContainer

    'REMARQUE: el Diseñador de Windows Form requiere el siguiente procedimiento
    'Se puede modificar utilizando el Diseñador de formularios de Windows.  
    '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 de cierre, maximizar/minimizar, tamaño ajustable, etc., y gestiona los eventos de 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 formularios de Windows requiere esta llamada.
        InitializeComponent()

        'Añada cualquier inicialización después de la llamada InitializeComponent()
    End Sub

Cualquier otro trabajo que haya que realizar en el constructor se puede hacer tras 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 anchura (292) y su altura (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 ha generado. 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

Añadamos al código de [Form1.vb] el código anterior y volvamos a compilar:

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 conocido. Esto simplemente significa que no hemos importado su espacio de nombres [System.Windows.Forms]. Añadamos la siguiente instrucción:

Imports System.Windows.Forms

y volvamos a compilar:

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

Esta vez 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 añadir 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 va a ejecutar es la clase con el nombre [form2].

5.2.6. Gestión de eventos

Una vez mostrado el formulario, la aplicación se pone a la escucha de los eventos que se producen en el formulario (clics, movimientos del ratón, etc.) y ejecuta aquellos que el formulario gestiona. En este caso, nuestro formulario no gestiona ningún evento que no sean los gestionados por 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 ningún sitio. El método dispose tampoco sirve para nada aquí. Lo mismo ocurre con algunos espacios de nombres (Collections, ComponentModel, Data) utilizados 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 añadir el nuestro, en particular para gestionar los eventos relacionados con los diferentes 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 vamos a crear 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, 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

A continuación, aparecerá la ventana de propiedades 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 de 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 la fuente de los caracteres en la ventana
Name
para establecer el nombre de la ventana

Aquí fijamos las propiedades Text y Name:

Text
Entradas y botones - 1
Name
frmSaisiesBoutons

Con la barra «Caja de herramientas»

  • seleccione los componentes que necesite
  • colóquelos en la ventana y asigne las dimensiones adecuadas
Una vez elegido el componente en la «caja de herramientas», utiliza la tecla
«Esc» para ocultar la barra de herramientas y, a continuación, arrastre
y ajuste el tamaño del componente. Haga lo mismo con los tres componentes
Requeridos: Label, TextBox, Button. Para alinear y
ajustar correctamente el tamaño de los componentes, utilice el menú Format:
El principio del formato es el siguiente:
  1. seleccione los diferentes componentes que desea formatear juntos (manteniendo pulsada la tecla Ctrl)
  2. seleccione el tipo de formato deseado
La opción Align le permite alinear componentes
La opción Make Same Size permite que
componentes tengan la misma altura o el mismo
ancho:
La opción «Horizontal Spacing» permite, por ejemplo,
alinear horizontalmente los componentes con
espacios entre ellos del mismo tamaño. 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, fije sus propiedades.
Para ello, haz clic con el botón derecho del ratón sobre el componente y
seleccione la opción Properties:
  • la etiqueta 1 (Label)
Seleccione el componente para acceder a
su ventana de propiedades. En ella,
modifique las siguientes propiedades:
nombre: lblSaisie, texto: Introducción
  • el campo de entrada 2 (TextBox)
Seleccione el componente para acceder a su
ventana de propiedades. En ella, modifique
las siguientes propiedades:
nombre: txtSaisie, texto: no introduzca nada
  • el botón 3 (Button):
nombre: cmdAfficher, texto: Mostrar
  • la propia ventana: name: 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. Gestión de eventos de un formulario

Veamos el código que ha generado 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 específica 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 centraremos en cómo gestionar 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 se muestra en negrita (en este caso, Click). Se trata del evento predeterminado del componente. Se puede acceder al controlador de este evento concreto haciendo doble clic sobre el componente en la ventana de diseño. VB.net genera entonces automáticamente el esqueleto del controlador de eventos en la ventana de código y sitúa al usuario sobre él. El acceso a los gestores 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 gestor de eventos o se sitúa 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 nombra el gestor del evento E del componente C. Se puede cambiar este nombre si nos conviene. Sin embargo, no es recomendable. Los desarrolladores de VB suelen mantener 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 gestiona el evento Click del componente btnAfficher. El gestor 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 se ha producido

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 introducido en el cuadro 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 gestionar los eventos

Para el botón btnAfficher, VS.NET ha generado 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, perdiendo así su asociación con el evento btnAfficher.Click. Esta asociación se realiza ahora 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. Por otra parte, el componente btnAfficher ya no necesita la palabra clave WithEvents:


    Friend btnAfficher As System.Windows.Forms.Button

¿Cuál es la diferencia entre ambos 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 gestionar 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 gestione 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 produce un evento concreto que puede interesar a otros objetos. Estos se suscriben al productor para recibir el evento (una temperatura que ha superado un umbral crítico, por ejemplo). Durante la ejecución de la aplicación, el productor del evento tendrá que 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 diferentes objetos consumidores de dicho evento. Tendremos ocasión de volver sobre 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 estudiados, 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 habituales con el fin de descubrir sus principales métodos y propiedades. Para cada aplicación, presentamos la interfaz gráfica y el código relevante, en particular los controladores de eventos.

5.4.1. Formulario

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 se produce incluso antes de que se muestre el formulario. El evento Closing se produce cuando el formulario se está cerrando. Este cierre aún se puede detener mediante programación. Creamos un formulario con el nombre Form1 sin componentes:

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 diferentes
eventos. El evento Closing se producirá cuando el usuario
cierre la ventana.
A continuación, le preguntamos si realmente desea salir de la aplicación:
Si responde «No», establecemos la propiedad Cancel del evento
CancelEventArgs que el método ha recibido 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 Label y cuadros de entrada TextBox

Ya nos hemos encontrado con estos dos componentes. Label es un componente de texto y TextBox un componente de campo de entrada. Su propiedad principal es Text, que designa bien el contenido del campo de entrada o bien el texto de la etiqueta. Esta propiedad es de lectura/escritura. El evento que se suele utilizar 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 seguir 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 gestores de eventos:


  ' clic en el botón Salir
    Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdQuitter.Click
        ' 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

    ' 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 cuadro de entrada
        txtSaisie.Text = ""
    End Sub

    ' se ha pulsado una tecla
    Private Sub txtSaisie_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
    Handles txtSaisie.KeyPress
        ' se comprueba qué tecla se ha pulsado
        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
Añade el contenido de 2 a 1

Para que un TextBox se convierta en multilínea, se configuran las siguientes propiedades del control:

 
para aceptar 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 Intro 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 útil es el que gestiona el clic en el botón [Ajouter] y el que gestiona la modificación del campo de entrada [txtAjout]:


    ' evt btnAjouter_Click
    Private Sub btnAjouter_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' se añade el contenido de txtAjout al de txtMultilignes
        txtMultilignes.Text &= txtAjout.Text
        txtAjout.Text = ""
    End Sub

    ' evt 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 Añadir
        btnAjouter.Enabled = txtAjout.Text.Trim() <> ""
    End Sub

5.4.3. listas desplegables ComboBox

Un componente ComboBox es una lista desplegable acompañada de un campo de entrada: el usuario puede elegir un elemento en (2) o escribir texto en (1). Existen tres tipos de ComboBox determinados por la propiedad Style:

 
lista no desplegable con cuadro de edición
DropDown
lista desplegable con cuadro de edición
DropDownList
lista desplegable sin área de edición

Por defecto, el tipo de un ComboBox es DropDown. Para obtener información sobre la clase ComboBox, escriba 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. Sea C un combo y C.Items 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)
añade el objeto o como último elemento del combo
C.AddRange(object() objets)
añade una matriz de objetos al final del combo
C.Insert(int i, object o)
añade 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 sorprender que un combo pueda contener objetos cuando normalmente contiene cadenas de caracteres. A nivel visual, 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 y que devuelve una cadena de caracteres «representativa» del objeto. El elemento seleccionado en el combo C es C.SelectedItem o C.Items(C.SelectedIndex), donde C.SelectedIndex es el número del elemento seleccionado, n.º que comienza en cero para el primer elemento.

Al seleccionar un elemento en la lista desplegable se produce el evento SelectedIndexChanged, que puede utilizarse para recibir una notificación del cambio de selección en el cuadro combinado. En la siguiente aplicación, utilizamos este evento para mostrar el elemento que se ha seleccionado en la lista.

Image

Solo mostramos el código relevante de la ventana. En el constructor del formulario rellenamos el cuadro combinado:


  Public Sub New()
    ' creación del formulario
    InitializeComponent()
    ' rellenar el cuadro combinado
    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 cuadro combinado 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 crear 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 añadir 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
traslada 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
vacía la lista 2
  • El usuario escribe texto en el campo 1. Lo añade a la lista 1 con el botón Ajouter (2). A continuación, se vacía el campo de entrada (1) y el usuario puede añadir 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 adecuado 5 o 6. El elemento transferido se añade 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 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 establecer 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 adicionales
        ' 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 de 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 Añadir 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 de transferencia de 1 a 2
        btn1TO2.Enabled = True
    End Sub

    ' cambio del elemento seleccionado sin 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 Añadir
    Private Sub btnAjouter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' se añade un nuevo elemento a la lista 1
        listBox1.Items.Add(txtSaisie.Text.Trim())
        ' borrado de la entrada
        txtSaisie.Text = ""
        ' La lista 1 no está vacía
        btnEffacer1.Enabled = True
        ' el foco vuelve al cuadro de entrada
        txtSaisie.Focus()
    End Sub

Cabe destacar el método Focus, que permite poner 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
        ' añadir 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 en la lista l1. Esto nos permite disponer de un único 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 no hay ningún elemento seleccionado actualmente. 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 algunos botones para reflejar el nuevo estado de las listas.

5.4.5. casillas de selección CheckBox, botones de radio 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 radio
2
CheckBox
chechBox1
chechBox2
chechBox3
3 casillas de selección
3
ListBox
lstValeurs
una lista

Si creamos los tres botones de radio 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 verificación o del botón de radio 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 único método para gestionar 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 radio o de la casilla de verificación
        ' ¿Es una casilla de verificació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 radio?
        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, a continuación, 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 origina el evento y el valor de su propiedad Checked. Al ejecutarlo, se observa que al hacer clic en un botón de radio se producen dos eventos CheckChanged: uno en el antiguo botón 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 control deslizante: el control deslizante 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 regulador horizontal
ReadOnly=true para impedir cualquier entrada
4
NumericUpDown
incrementador
permite fijar el valor del control deslizante 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 están representados por las propiedades Minimum y Maximum y tienen por defecto los valores 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 haya hecho clic, denominado SmallChange, cuyo valor por defecto 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 por defecto es 10.
  • Al hacer clic en el extremo superior de un control deslizante vertical, su valor disminuye. Esto puede sorprender al usuario medio, que normalmente espera que el valor «aumente». Este problema se soluciona asignando un valor negativo a las propiedades SmallChange y LargeChange
  • Estas cinco propiedades (Value, Minimum, Maximum, SmallChange, LargeChange) son accesibles en lectura y escritura.
  • El evento principal del variador es el que señala un cambio de valor: el evento Scroll.

Un componente NumericUpDown se encuentra cerca del variador: también tiene las propiedades Minimum, Maximum y Value, con valores predeterminados de 0, 100 y 0, respectivamente. Pero aquí, la propiedad Value se muestra en un cuadro de entrada que forma parte integrante del control. El usuario puede modificar este valor por sí mismo, salvo que se haya establecido la propiedad ReadOnly del control en verdadero. El valor del incremento viene determinado por la propiedad Increment, cuyo valor por defecto es 1. El evento principal del componente NumericUpDown es el que señala un cambio de valor: el evento ValueChanged. El código útil de nuestra aplicación es el siguiente:

El formulario se formatea durante su construcción:


    ' constructor
    Public Sub New()
        ' creación inicial del formulario
        InitializeComponent()
        ' se le dan 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 asigna a TextBox el valor del variador 1
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

El gestor que sigue 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 gestor que sigue 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 inhibe cualquier cambio en el regulador 2
        ' obligándolo a mantener el valor del variador 1
        e.NewValue = hScrollBar1.Value
    End Sub

El gestor que supervisa las variaciones del control incrémenteur:


    ' gestión 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

Cuando se dibuja 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 provocan eventos en el contenedor por el que se desplaza.

 
el ratón acaba de entrar en el área del control
MouseLeave
el ratón acaba de salir del área del control
MouseMove
el ratón se mueve dentro del área de control
MouseDown
Pulsación del botón izquierdo del ratón
MouseUp
Suelta el botón izquierdo del ratón
DragDrop
El usuario suelta un objeto sobre el control
DragEnter
El usuario entra en el á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 muestra un programa que permite comprender mejor en qué momentos se producen 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 controladores de eventos son los siguientes. Para seguir los movimientos del ratón en los tres controles, solo se escribe un único controlador:


    ' 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

Hay que tener en cuenta 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 código, cuando se pasa el ratón del formulario al botón, se ve claramente el cambio de coordenadas. Para ver mejor estos cambios de á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 ámbito del control. Así, en nuestro ejemplo, hemos establecido el cursor en Default para el propio formulario, en Hand para la lista 2 y en No para el botón 3, tal y como muestran las capturas de pantalla siguientes.

Image

Image

Image

En el método [InitializeComponent], el código generado por estas opciones es el siguiente:


        Me.lstEvts.Cursor = System.Windows.Forms.Cursors.Hand
        Me.btnEffacer.Cursor = System.Windows.Forms.Cursors.No

Por otra parte, para detectar las entradas y salidas del ratón en la lista 2, procesamos los eventos MouseEnter y MouseLeave de esta misma lista:


    ' evt 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 gestionar 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 gestor de clics 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ú

Veamos ahora 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 tienen 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 de menú

Las propiedades de las diferentes opciones de menú de nuestro ejemplo son las siguientes:

Name
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 este modo, se crea un menú vacío que se inserta en el formulario con casillas vacías tituladas «Type Here». Solo hay que indicar en ellas las diferentes opciones del menú:

Image

Para insertar un separador entre dos opciones, como se ha hecho anteriormente entre las opciones B1 y B2, colóquese en la ubicación del separador en el menú, haga clic con el botón derecho y seleccione la opción Insert Separator:

Image

Si se inicia la aplicación con Ctrl+F5, aparece un formulario con un menú que, por el momento, no realiza ninguna acción. Las opciones del menú se tratan como componentes: tienen propiedades y eventos. En [fenêtre de code], seleccione el componente mnuA1 y, a continuación, 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 así para todas las opciones del menú. En este caso, se puede utilizar el mismo procedimiento para todas las opciones. Por lo tanto, renombramos el procedimiento anterior como affiche y declaramos los eventos que gestiona:


    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 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 útil 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 asocia el menú al formulario:


    Me.Menu = Me.mainMenu1

5.7. Componentes no visuales

Ahora nos centramos 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 sitúan en una zona separada en la parte inferior 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 cuadro de entrada
        txtTexte.Text = ""
    End Sub

La clase SaveFileDialog se define de la siguiente manera:

Image

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

string Filter
los tipos de archivo que aparecen en la lista desplegable de tipos de archivo del cuadro de diálogo
int FilterIndex
el número del tipo de archivo propuesto por defecto en la lista anterior. Empieza por 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 predeterminado viene fijado por FilterIndex
2
carpeta actual, fijado por InitialDirectory si se ha rellenado esta propiedad
3
nombre del archivo elegido o introducido 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 en él
                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 establece la carpeta inicial como la carpeta que contiene el ejecutable de la aplicación:
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • Se establecen los tipos de archivos que se van a presentar
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"

Cabe destacar la sintaxis de los filtros filtro1|filtro2|..|filtro con filtroi= Texto|plantilla de archivo. Aquí el usuario podrá elegir entre los archivos *.txt y *.*.

  • Se establece el tipo de archivo que se mostrará al principio
        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 mediante 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 va a crear se encuentra ahora en la propiedad FileName del objeto saveFileDialog1. A continuación, se vuelve a la creación clásica de un archivo de texto. En él se escribe el contenido de TextBox: txtTexte.Text, gestionando al mismo tiempo las excepciones que puedan producirse.

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 archivo propuestos en la lista desplegable de tipos de archivo del cuadro de diálogo
int FilterIndex
el número del tipo de archivo propuesto por defecto en la lista anterior. Empieza por 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 de guardado. Devuelve un resultado de tipo DialogResult.

El método ShowDialog muestra un cuadro de diálogo similar al siguiente:

1
lista desplegable creada a partir de la propiedad Filter. El tipo de archivo propuesto por defecto viene determinado por FilterIndex
2
carpeta actual, fijada por InitialDirectory si se ha rellenado esta propiedad
3
nombre del archivo elegido o introducido 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 establecen 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 principio
            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 especifica el nombre del archivo que desea abrir y sale del cuadro de diálogo pulsando 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 continuación, se vuelve a la lectura clásica 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 txtTexte. Se gestionan las excepciones que puedan producirse.

5.7.2. Cuadros de diálogo FontColor y ColorDialog

Continuamos con el ejemplo anterior presentando dos nuevos botones:

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 los caracteres de TextBox

Colocamos en el formulario un control ColorDialog y un control FontDialog:

Image

Las clases FontDialog y ColorDialog tienen 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. Tenemos los elementos para gestionar 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 marcha:

Image

El cronómetro parado:

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. Este 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 producido al final de Interval milisegundos
Enabled
activa (true) o desactiva (false) el temporizador

En nuestro ejemplo, el temporizador se llama timer1 y timer1.Interval se establece en 1000 ms (1 s). Por lo tanto, el evento Tick se producirá cada segundo. El clic en el botón Parar/Iniciar se gestiona mediante el siguiente procedimiento:


    Private Sub btnArretMarche_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnArretMarche.Click
        ' ¿parar o continuar?
        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
            ' cambiar el texto del botón
            btnArretMarche.Text = "Arrêt"
            ' fin
            Return
        End If        '
        If btnArretMarche.Text = "Arrêt" Then
            ' parada del 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 Parar/Iniciar es «Parar» o «Iniciar». Por lo tanto, es necesario realizar una comprobación de este texto para saber qué hacer.

  • En el caso de «Marcha», se anota la hora de inicio en una variable que es una variable global del objeto formulario, se inicia el temporizador (Enabled=true) y el texto del botón cambia a «Parada».
  • En el caso de «Parada», se detiene el temporizador (Enabled=false) y se cambia el texto del botón a «Marcha».

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 anterior es conocido en todos los métodos de la clase. Nos queda por tratar el evento Tick en el objeto timer1, evento que se produce 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

Se calcula el tiempo transcurrido desde el momento en que se puso en marcha el cronómetro. Se obtiene un objeto de tipo TimeSpan que representa una duración en el tiempo. Esta debe mostrarse en el cronómetro en 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 obtener una visualización de 2 dígitos.

5.8. El ejemplo IMPOTS

Retomamos la aplicación IMPOTS, que ya hemos tratado dos veces. Ahora le añadimos una interfaz gráfica:

Los controles son los siguientes

n.º
tipo
nombre
función
1
RadioButton
rdOui
marcado si está casado
2
RadioButton
rdNon
marcado 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
Importe 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 cargarse
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 señala el error:

Image

El programa se muestra a continuación. 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 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 fiscal
    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 introducción 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()
      ' volver 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 añadir una referencia (un ensamblaje), hacemos clic con el botón derecho del ratón en el conjunto clave References situado arriba, seleccionamos la opción [Ajouter une référence] y designamos el ensamblaje [impots.dll] que nos hemos encargado 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 ensamblado [impots.dll] en el proyecto, la clase [impots] pasa a ser conocida 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, se hace clic con el botón derecho del ratón sobre el proyecto, se selecciona la opción [Ajouter/Ajouter un élément existant] y se indica el archivo impots.vb.