Skip to content

5. Interfaces graphiques avec VB.NET et VS.NET

Nous nous proposons ici de montrer comment construire des interfaces graphiques avec VB.NET. Nous voyons tout d'abord quelles sont les classes de base de la plate-forme .NET qui nous permettent de construire une interface graphique. Nous n'utilisons dans un premier temps aucun outil de génération automatique. Puis nous utiliserons Visual Studio.NET (VS.NET), un outil de développement de Microsoft facilitant le développement d'applications avec les langages .NET et notamment la construction d'interfaces graphiques. La version VS.NET utilisée est la version anglaise.

5.1. Les bases des interfaces graphiques

5.1.1. Une fenêtre simple

Considérons le code suivant :


' options
Option Strict On
Option Explicit On 

' espaces de noms
Imports System
Imports System.Drawing
Imports System.Windows.Forms

' la classe formulaire
Public Class Form1
    Inherits Form

    ' le constructeur
    Public Sub New()
        ' titre de la fenêtre
        Me.Text = "Mon premier formulaire"
        ' dimensions de la fenêtre
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub

    ' fonction de test
    Public Shared Sub Main(ByVal args() As String)
        ' on affiche le formulaire
        Application.Run(New Form1)
    End Sub
End Class

Le code précédent est compilé puis exécuté

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

dos>frm1

L'exécution affiche la fenêtre suivante :

Image

Une interface graphique dérive en général de la classe de base System.Windows.Forms.Form :

Public Class Form1
    Inherits Form

La classe de base Form définit une fenêtre de base avec des boutons de fermeture, agrandissement/réduction, une taille ajustable, ... et gère les événements sur ces objets graphiques. Ici nous spécialisons la classe de base en lui fixant un titre et ses largeur (300) et hauteur (100). Ceci est fait dans son constructeur :


    Public Sub New()
        ' titre de la fenêtre
        Me.Text = "Mon premier formulaire"
        ' dimensions de la fenêtre
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub

Le titre de la fenêtre est fixée par la propriété Text et les dimensions par la propriété Size. Size est défini dans l'espace de noms System.Drawing et est une structure. La procédure Main lance l'application graphique de la façon suivante :


        Application.Run(New Form1)

Un formulaire de type Form1 est créé et affiché, puis l'application se met à l'écoute des événements qui se produisent sur le formulaire (clics, déplacements de souris, ...) et fait exécuter ceux que le formulaire gère. Ici, notre formulaire ne gère pas d'autre événement que ceux gérés par la classe de base Form (clics sur boutons fermeture, agrandissement/réduction, changement de taille de la fenêtre, déplacement de la fenêtre, ...).

5.1.2. Un formulaire avec bouton

Ajoutons maintenant un bouton à notre fenêtre :


' options
Option Strict On
Option Explicit On 

' espaces de noms
Imports System
Imports System.Drawing
Imports System.Windows.Forms

' la classe formulaire
Public Class Form1
    Inherits Form

    ' attributs
    Private cmdTest As Button

    ' le constructeur
    Public Sub New()
        ' le titre
        Me.Text = "Mon premier formulaire"
        ' les dimensions
        Me.Size = New System.Drawing.Size(300, 100)
        ' un bouton
        ' création
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' taille
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' libellé
        cmdTest.Text = "Test"
        ' gestionnaire d'évt
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' ajout bouton au formulaire
        Me.Controls.Add(cmdTest)
    End Sub

    ' gestionnaire d'événement
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' il y a eu un clic sur le bouton - on le dit
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    ' fonction de test
    Public Shared Sub Main(ByVal args() As String)
        ' on affiche le formulaire
        Application.Run(New Form1)
    End Sub
End Class

Nous avons rajouté au formulaire un bouton :


        ' un bouton
        ' création
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' taille
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' libellé
        cmdTest.Text = "Test"
        ' gestionnaire d'évt
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' ajout bouton au formulaire
        Me.Controls.Add(cmdTest)

La propriété Location fixe les coordonnées (110,20) du point supérieur gauche du bouton à l'aide d'une structure Point. Les largeur et hauteur du bouton sont fixées à (80,30) à l'aide d'une structure Size. La propriété Text du bouton permet de fixer le libellé du bouton. La classe bouton possède un événement Click défini comme suit :

Public Event Click As EventHandler

EventHandler est fonction "déléguée" ayant la signature suivante :

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

Cela signifie que le gestionnaire de l'événement [Click] sur le bouton devra avoir la signature du délégué [EventHandler]. Ici, lors d'un clic sur le bouton cmdTest, la méthode cmdTest_Click sera appelée. Celle-ci est définie comme suit conformément au modèle EventHandler précédent :


    ' gestionnaire d'événement
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' il y a eu un clic sur le bouton - on le dit
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

On se contente d'afficher un message :

Image

La classe est compilée et exécutée :

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 classe MessageBox sert à afficher des messages dans une fenêtre. Nous avons utilisé ici le constructeur

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
le message à afficher
caption
le titre de la fenêtre
buttons
les boutons présents dans la fenêtre
icon
l'icone présente dans la fenêtre

Le paramètre buttons peut prendre ses valeurs parmi les constantes suivantes :

constante
boutons
   AbortRetryIgnore 
  OK 
    OKCancel 
    RetryCancel 
    YesNo 
    YesNoCancel 

Le paramètre icon peut prendre ses valeurs parmi les constantes suivantes :

Asterisk
Error
idem Stop
Exclamation
idem Warning
Hand
Information
idem Asterisk
None
Question
Stop
idem Hand
Warning

 

La méthode Show est une méthode statique qui rend un résultat de type System.Windows.Forms.DialogResult qui est une énumération :


public enum System.Windows.Forms.DialogResult 
{
    Abort = 0x00000003, 
    Cancel = 0x00000002, 
    Ignore = 0x00000005, 
    No = 0x00000007, 
    None = 0x00000000, 
    OK = 0x00000001, 
    Retry = 0x00000004, 
    Yes = 0x00000006, 
}

Pour savoir sur quel bouton a appuyé l'utilisateur pour fermer la fenêtre de type MessageBox on écrira :

dim res as DialogResult=MessageBox.Show(..)
if res=DialogResult.Yes then
 ' il a appuyé sur le bouton oui
...
end if

5.2. Construire une interface graphique avec Visual Studio.NET

Nous reprenons certains des exemples vus précédemment en les construisant maintenant avec Visual Studio.NET.

5.2.1. Création initiale du projet

  1. Lancez VS.NET et prendre l'option Fichier/Nouveau/Projet

Image

  1. donnez les caractéristiques de votre projet
    • sélectionnez le type de projet que vous voulez construire, ici un projet VB.NET (1)
    • sélectionnez le type d'application que vous voulez construire, ici une application windows (2)
    • indiquez dans quel dossier vous voulez placer le sous-dossier du projet (3)
    • indiquez le nom du projet (4). Ce sera également le nom du dossier qui contiendra les fichiers du projet
    • le nom de ce dossier est rappelé en (5)
  1. Un certain nombre de dossiers et de fichiers sont alors créés sous le dossier i4 :
sous-dossiers du dossier projet1

De ces fichiers, seul un est intéressant, le fichier form1.cs qui est le fichier source associé au formulaire créé par VS.NET. Nous y reviendrons.

5.2.2. Les fenêtre de l'interface de VS.NET

L'interface de VS.NET laisse maintenant apparaître certains éléments de notre projet i4 :

Nous avons une fenêtre de conception de l'interface graphique :

En prenant des contrôles dans la barre d'outils (toolbox 2) et en les déposant sur la surface de la fenêtre (1), nous pouvons construire une interface graphique. Si nos amenons la souris sur la "toolbox" celle-ci s'agrandit et laisse apparaître un certain nombre de contrôles :

Image

Pour l'instant, nous n'en utilisons aucun. Toujours sur l'écran de VS.NET, nous trouvons la fenêtre de l'explorateur de solutions "Explorer Solution" :

Image

Dans un premier temps, nous ne nous servirons peu de cette fenêtre. Elle montre l'ensemble des fichiers formant le projet. Un seul d'entre-eux nous intéresse : le fichier source de notre programme, ici Form1.vb. En cliquant droit sur Form1.vb, on obtient un menu permettant d'accéder soit au code source de notre interface graphique (Afficher le code) soit à l'interface graphique elle-même (Concepteur de vues) :

Image

On peut accèder à ces deux entités directement à partir de la fenêtre "Solution Explorer" :

 

Les fenêtres ouvertes "s'accumulent" dans la fenêtre principale de conception :

Image

Ici Form1.vb[Design] désigne la fenêtre de conception et Form1.vb la fenêtre de code. Il suffit de cliquer sur l'un des onglets pour passer d'une fenêtre à l'autre. Une autre fenêtre importante présente sur l'écran de VS.NET est la fenêtre des propriétés :

Image

Les propriétés exposées dans la fenêtre sont celles du contrôle actuellement sélectionné dans la fenêtre de conception graphique. On a accès à différentes fenêtres du projet avec le menu Affichage :

Image

On y retrouve les fenêtres principales qui viennent d'être décrites ainsi que leurs raccourcis clavier.

5.2.3. Exécution d'un projet

Alors même que nous n'avons écrit aucun code, nous avons un projet exécutable. Faites F5 ou Déboguer/Démarrer pour l'exécuter. Nous obtenons la fenêtre suivante :

Image

Cette fenêtre peut être agrandie, mise en icône, redimensionnée et fermée.

5.2.4. Le code généré par VS.NET

Regardons le code (Affichage/Code) de notre application :


Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Code généré par le Concepteur Windows Form "

    Public Sub New()
        MyBase.New()

        'Cet appel est requis par le Concepteur Windows Form.
        InitializeComponent()

        'Ajoutez une initialisation quelconque après l'appel InitializeComponent()

    End Sub

    'La méthode substituée Dispose du formulaire pour nettoyer la liste des composants.
    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

    'Requis par le Concepteur Windows Form
    Private components As System.ComponentModel.IContainer

    'REMARQUE : la procédure suivante est requise par le Concepteur Windows Form
    'Elle peut être modifiée en utilisant le Concepteur Windows Form.  
    'Ne la modifiez pas en utilisant l'éditeur de code.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.Text = "Form2"
    End Sub

#End Region

End Class

Une interface graphique dérive de la classe de base System.Windows.Forms.Form :

Public Class Form1
    Inherits System.Windows.Forms.Form

La classe de base Form définit une fenêtre de base avec des boutons de fermeture, agrandissement/réduction, une taille ajustable, ... et gère les événements sur ces objets graphiques. Le constructeur du formulaire utilise une méthode InitializeComponent dans laquelle les contrôles du formulaires sont créés et initialisés.


    Public Sub New()
        MyBase.New()

        'Cet appel est requis par le Concepteur Windows Form.
        InitializeComponent()

        'Ajoutez une initialisation quelconque après l'appel InitializeComponent()
    End Sub

Tout autre travail à faire dans le constructeur peut être fait après l'appel à InitializeComponent. La méthode 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

fixe le titre de la fenêtre "Form1", sa largeur (292) et sa hauteur (53). Le titre de la fenêtre est fixée par la propriété Text et les dimensions par la propriété Size. Size est défini dans l'espace de noms System.Drawing et est une structure. Pour exécuter cette application, il nous faut définir le module principal du projet. Pou cela, nous utilisons l'option [Projets/Propriétés] :

Image

Dans [Objet de démarrage], nous indiquons [Form1] qui est le formulaire que nous venons de créer. Pour lancer l'exécution, nous utilisons l'option [Déboguer/Démarrer] :

Image

5.2.5. Compilation dans une fenêtre dos

Maintenant, essayons de compiler et exécuter cette application dans une fenêtre 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'.

Le compilateur indique qu'il ne trouve pas la procédure [Main]. En effet, VS.NET ne l'a pas générée. Nous l'avons cependant déjà rencontrée dans les exemples précédents. Elle a la forme suivante :


    Shared Sub Main()
        ' on lance l'appli
        Application.Run(New Form1) ' où Form1 est le formulaire
    End Sub

Rajoutons dans le code de [Form1.vb] le code précédent et recompilons :

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

Cette fois-ci, le nom [Application] n'est pas connu. Cela veut simplement dire que nous n'avons pas importé son espace de noms [System.Windows.Forms]. Rajoutons l'instruction suivante :

Imports System.Windows.Forms

puis recompilons :

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

Cette fois-ci, ça passe. Exécutons :

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

dos>form1

Image

Un formulaire de type Form1 est créé et affiché. On peut éviter l'ajout de la procédure [Main] en utilisant l'option /m du compilateur qui permet de préciser la classe à exécuter dans le cas où celle-ci hérite de System.Windows.Form :

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

L'option /m:form2 indique que la classe à exécuter est la classe de nom [form2].

5.2.6. Gestion des événements

Une fois le formulaire affiché, l'application se met à l'écoute des événements qui se produisent sur le formulaire (clics, déplacements de souris, ...) et fait exécuter ceux que le formulaire gère. Ici, notre formulaire ne gère pas d'autre événement que ceux gérés par la classe de base Form (clics sur boutons fermeture, agrandissement/réduction, changement de taille de la fenêtre, déplacement de la fenêtre, ...). Le formulaire généré utilise un attribut components qui n'est utilisé nulle part. La méthode dispose ne sert également à rien ici. Il en de même de certains espaces de noms (Collections, ComponentModel, Data) utilisés et de celui défini pour le projet projet1. Aussi, dans cet exemple le code peut être simplifié à ce qui suit :


Imports System
Imports System.Drawing
Imports System.Windows.Forms

Public Class Form1
    Inherits System.Windows.Forms.Form

    ' constructeur
    Public Sub New()
        ' construction du formulaire avec ses composants
        InitializeComponent()
    End Sub

    Private Sub InitializeComponent()
        ' taille de la fenêtre
        Me.Size = New System.Drawing.Size(300, 300)
        ' titre de la fenêtre
        Me.Text = "Form1"
    End Sub

    Shared Sub Main()
        ' on lance l'appli
        Application.Run(New Form1)
    End Sub
End Class

5.2.7. Conclusion

Nous accepterons maintenant tel quel le code généré par VS.NET et nous contenterons d'y ajouter le nôtre notamment pour gérer les événements liés aux différents contrôles du formulaire.

5.3. Fenêtre avec champ de saisie, bouton et libellé

5.3.1. Conception graphique

Dans l'exemple précédent, nous n'avions pas mis de composants dans la fenêtre. Nous commençons un nouveau projet appelé interface2. Pour cela nous suivons la procédure explicitée précédemment pour créer un projet :

Image

Construisons maintenant une fenêtre avec un bouton, un libellé et un champ de saisie :

Les champs sont les suivants :

nom
type
rôle
1
lblSaisie
Label
un libellé
2
txtSaisie
TextBox
une zone de saisie
3
btnAfficher
Button
pour afficher dans une boîte de dialogue le contenu de la zone de saisie txtSaisie

On pourra procéder comme suit pour construire cette fenêtre : cliquez droit dans la fenêtre en-dehors de tout composant et choisissez l'option Properties pour avoir accès aux propriétés de la fenêtre :

Image

La fenêtre de propriétés apparaît alors sur la droite :

Image

Certaines de ces propriétés sont à noter :

BackColor
pour fixer la couleur de fond de la fenêtre
ForeColor
pour fixer la couleur des dessins ou du texte sur la fenêtre
Menu
pour associer un menu à la fenêtre
Text
pour donner un titre à la fenêtre
FormBorderStyle
pour fixer le type de fenêtre
Font
pour fixer la police de caractères des écritures dans la fenêtre
Name
pour fixer le nom de la fenêtre

Ici, nous fixons les propriétés Text et Name :

Text
Saisies & boutons - 1
Name
frmSaisiesBoutons

A l'aide de la barre "Boîte à outils"

  • sélectionnez les composants dont vous avez besoin
  • déposez-les sur la fenêtre et donnez-leur leurs bonnes dimensions
Une fois le composant choisi dans le "toolbox", utilisez la touche
"Echap" pour faire disparaître la barre d'outils, puis déposez
et dimensionnez le composant. Faites-le pour les trois composants
nécessaires : Label, TextBox, Button. Pour aligner et
dimensionner correctement les composants, utilisez le menu Format :
Le principe du formatage est le suivant :
  1. sélectionnez les différents composants à formater ensemble (touche Ctrl appuyée)
  2. sélectionnez le type de formatage désiré
L'option Align vous permet d'aligner des composants
L'option Make Same Size permet de faire que des
composants aient la même hauteur ou la même
largeur :
L'option Horizontal Spacing permet par exemple
d'aligner horizontalement des composants avec
des intervalles entre eux de même taille. Idem
pour l'option Vertical Spacing pour aligner verticalement.
L'option Center in Form permet de centrer
un composant horizontalement ou
verticalement dans la fenêtre :
Une fois que les composants sont correctement
placés sur la fenêtre, fixez leurs propriétés.
Pour cela, cliquez droit sur le composant et
prenez l'option Properties :
  • l'étiquette 1 (Label)
Sélectionnez le composant pour avoir
sa fenêtre de propriétés. Dans celle-ci,
modifiez les propriétés suivantes :
name : lblSaisie, text : Saisie
  • le champ de saisie 2 (TextBox)
Sélectionnez le composant pour avoir sa
fenêtre de propriétés. Dans celle-ci, modifiez
les propriétés suivantes :
name : txtSaisie, text : ne rien mettre
  • le bouton 3 (Button) :
name : cmdAfficher, text : Afficher
  • la fenêtre elle-même : name : frmSaisies&Boutons,
text : Saisies & boutons - 1
Nous pouvons exécuter (ctrl-F5) notre projet
pour avoir un premier aperçu de la fenêtre
en action :

Fermez la fenêtre. Il nous reste à écrire la procédure liée à un clic sur le bouton Afficher.

5.3.2. Gestion des événements d'un formulaire

Regardons le code qui a été généré par le concepteur graphique :


...

Public Class frmSaisiesBoutons
  Inherits System.Windows.Forms.Form

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

  ' constructeur
  Public Sub New()
    InitializeComponent()
  End Sub

...

  ' initialisation des composants
    Private Sub InitializeComponent()
...
    End Sub
End Class

On notera tout d'abord la déclaration particulière des composants :

  • le mot clé Friend indique une visibilité du composant qui s'étend à toutes les classes du projet
  • le mot clé WithEvents indique que le composant génère des événements. Nous nous intéressons maintenant à la façon de gérer ces événements

Faites afficher la fenêtre de code du formulaire (Affichage/code ou F7) :

La fenêtre ci-dessus présente deux listes déroulantes (1) et (2). La liste (1) est la liste des composants du formulaire :

Image

La liste(2) la liste des événements associés au composant sélectionné dans (1) :

Image

L'un des événements associés au composant est affiché en gras (ici Click). C'est l'événement par défaut du composant. L'accès au gestionnaire de cet événement particulier peut se faire en double-cliquant sur le composant dans la fenêtre de conception. VB.net génère alors automatiquement le squelette du gestionnaire d'événement dans la fenêtre de code et positionne l'utilisateur dessus. L'accès aux gestionnaires des autres événements se fait lui dans la fenêtre de code, en sélectionnant le composant dans la liste (1) et l'événement dans (2). VB.net génère alors le squelette du gestionnaire d'événement ou se positionne dessus s'il était déjà généré :


    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

Par défaut, VB.net C_E nomme le gestionnaire de l'événement E du composant C. On peut changer ce nom si cela nous convient. C'est cependant déconseillé. Les développeurs VB gardent en général le nom généré par VB ce qui donne une cohérence du nommage dans tous les programmes VB. L'association de la procédure btnAfficher_Click à l'événement Click du composant btnAfficher ne se fait par le nom de la procédure mais par le mot clé handles :


Handles btnAfficher.Click

Ci-dessus, le mot clé handles précise que la procédure gère l'événement Click du composant btnAfficher. Le gestionnaire d'événement précédent a deux paramètres :

sender
l'objet à la source de l'événement (ici le bouton)
e
un objet EventArgs qui détaille l'événement qui s'est produit

Nous n'utiliserons aucun de ces paramètres ici. Il ne nous reste plus qu'à compléter le code. Ici, nous voulons présenter une boîte de dialogue avec dedans le contenu du champ txtSaisie :


    Private Sub btnAfficher_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' on affiche le texte qui a été saisi dans la boîte de saisie TxtSaisie
        MessageBox.Show("texte saisi= " + txtsaisie.Text, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

Si on exécute l'application on obtient la chose suivante :

Image

5.3.3. Une autre méthode pour gérer les événements

Pour le bouton btnAfficher, VS.NET a généré le code suivant :


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

...

    ' gestionnaire d'évt clic sur bouton cmdAfficher
    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

Nous pouvons associer la procédure btnAfficher_Click à l'événement Click du bouton btnAfficher d'une autre façon :


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

...

    ' gestionnaire d'évt clic sur bouton cmdAfficher
    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
    End Sub

La procédure btnAfficher_Click a perdu le mot clé Handles perdant ainsi son association avec l'événement btnAfficher.Click. Cette association est désormais faite à l'aide du mot clé AddHandler :


        AddHandler btnAfficher.Click, AddressOf btnAfficher_Click

Le code ci-dessus qui sera placé dans la procédure InitializeComponent du formulaire, associe à l'événement btnAfficher.Click la procédure portant le nom btnAfficher_Click. Par ailleurs, le composant btnAfficher n'a plus besoin du mot clé WithEvents :


    Friend btnAfficher As System.Windows.Forms.Button

Quelle est la différence entre les deux méthodes ?

  • le mot clé handles ne permet d'associer un événement à une procédure qu'au moment de la conception. Le concepteur sait à l'avance qu'une procédure P doit gérer les événements E1, E2, ... et il écrit le code

    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) handles E1, E2, ..., En

Il est en effet possible pour une procédure de gérer plusieurs événements.

  • le mot clé addhandler permet d'associer un événement à une procédure au moment de l'exécution. Ceci est utile dans un cadre producteur-consommateur d'événements. Un objet produit un événement particulier susceptible d'intéresser d'autres objets. Ceux-ci s'abonnent auprès du producteur pour recevoir l'événement (une température ayant dépassé un seuil critique, par exemple). Au cours de l'exécution de l'application, le producteur de l'événement sera amené à exécuter différentes instructions :
Addhandler E, AddressOf P1
Addhandler E, AddressOf P2
...
Addhandler E, AddressOf Pn

où E est l'événement produit par le producteur et Pi des procédures appartenant aux différents objets consommateurs de cet événement. Nous aurons l'occasion de revenir sur une application producteur-consommateur d'événements dans un prochain chapitre.

5.3.4. Conclusion

Des deux projets étudiés, nous pouvons conclure qu'une fois l'interface graphique construite avec VS.NET, le travail du développeur consiste à écrire les gestionnaires des événements qu'il veut gérer pour cette interface graphique. Nous ne présenterons désormais que le code de ceux-ci.

5.4. Quelques composants utiles

Nous présentons maintenant diverses applications mettant en jeu les composants les plus courants afin de découvrir les principales méthodes et propriétés de ceux-ci. Pour chaque application, nous présentons l'interface graphique et le code intéressant, notamment les gestionnaires d'événements.

5.4.1. formulaire Form

Nous commençons par présenter le composant indispensable, le formulaire sur lequel on dépose des composants. Nous avons déjà présenté quelques-unes de ses propriétés de base. Nous nous attardons ici sur quelques événements importants d'un formulaire.

 
le formulaire est en cours de chargement
Closing
le formulaire est en cours de fermeture
Closed
le formulaire est fermé

L'événement Load se produit avant même que le formulaire ne soit affiché. L'événement Closing se produit lorsque le formulaire est en cours de fermeture. On peut encore arrêter cette fermeture par programmation. Nous construisons un formulaire de nom Form1 sans composant :

Image

Nous traitons les trois événements précédents :


    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' chargement initial du formulaire
        MessageBox.Show("Evt Load", "Load")
    End Sub

    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        ' le formulaire est en train de se fermer
        MessageBox.Show("Evt Closing", "Closing")
        ' on demande confirmation
        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
        ' le formulaire est en train de se fermer
        MessageBox.Show("Evt Closed", "Closed")
    End Sub
Nous utilisons la fonction MessageBox pour être averti des différents
événements. L'événement Closing va se produire lorsque l'utilisateur
ferme la fenêtre.
Nous lui demandons alors s'il veut vraiment quitter l'application :
S'il répond Non, nous fixons la propriété Cancel de l'événement
CancelEventArgs e que la méthode a reçu en paramètre.
Si nous mettons cette propriété à False, la fermeture
de la fenêtre est abandonnée, sinon elle se poursuit :

5.4.2. étiquettes Label et boîtes de saisie TextBox

Nous avons déjà rencontré ces deux composants. Label est un composant texte et TextBox un composant champ de saisie. Leur propriété principale est Text qui désigne soit le contenu du champ de saisie ou le texte du libellé. Cette propriété est en lecture/écriture. L'événement habituellement utilisé pour TextBox est TextChanged qui signale que l'utilisateur à modifié le champ de saisie. Voici un exemple qui utilise l'événement TextChanged pour suivre les évolutions d'un champ de saisie :

type
nom
rôle
1
TextBox
txtSaisie
champ de saisie
2
Label
lblControle
affiche le texte de 1 en temps réel
3
Button
cmdEffacer
pour effacer les champs 1 et 2
4
Button
cmdQuitter
pour quitter l'application

Le code pertinent de cette application est celui des gestionnaires d'événements :


  ' clic sur btn quitter
    Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdQuitter.Click
        ' clic sur bouton Quitter - on quitte l'application
        Application.Exit()
    End Sub

    ' modification champ txtSaisie
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' le contenu du TextBox a changé - on le copie dans le Label lblControle
        lblControle.Text = txtSaisie.Text
    End Sub

    ' clic sur btn effacer
    Private Sub cmdEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdEffacer.Click
        ' on efface le contenu de la boîte de saisie
        txtSaisie.Text = ""
    End Sub

    ' une touche a été tapée
    Private Sub txtSaisie_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
    Handles txtSaisie.KeyPress
        ' on regarde quelle touche a été tapée
        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

On notera la façon de terminer l'application dans la procédure cmdQuitter_Click : Application.Exit(). L'exemple suivant utilise un TextBox multilignes :

La liste des contrôles est la suivante :

type
nom
rôle
1
TextBox
txtMultiLignes
champ de saisie multilignes
2
TextBox
txtAjout
champ de saisie monoligne
3
Button
btnAjouter
Ajoute le contenu de 2 à 1

Pour qu'un TextBox devienne multilignes on positionne les propriétés suivantes du contrôle :

 
pour accepter plusieurs lignes de texte
ScrollBars=( None, Horizontal, Vertical, Both)
pour demander à ce que le contrôle ait des barres de défilement (Horizontal, Vertical, Both) ou non (None)
AcceptReturn=(True, False)
si égal à true, la touche Entrée fera passer à la ligne
AcceptTab=(True, False)
si égal à true, la touche Tab générera une tabulation dans le texte

Le code utile est celui qui traite le clic sur le bouton [Ajouter] et celui qui traite la modification du champ de saisie [txtAjout] :


    ' évt btnAjouter_Click
    Private Sub btnAjouter_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' ajout du contenu de txtAjout à celui 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
        ' on fixe l'état du bouton Ajouter
        btnAjouter.Enabled = txtAjout.Text.Trim() <> ""
    End Sub

5.4.3. listes déroulantes ComboBox

Un composant ComboBox est une liste déroulante doublée d'une zone de saisie : l'utilisateur peut soit choisir un élément dans (2) soit taper du texte dans (1). Il existe trois sortes de ComboBox fixées par la propriété Style :

 
liste non déroulante avec zone d'édition
DropDown
liste déroulante avec zone d'édition
DropDownList
liste déroulante sans zone d'édition

Par défaut, le type d'un ComboBox est DropDown. Pour découvrir la classe ComboBox, tapez ComboBox dans l'index de l'aide (Aide/Index). La classe ComboBox a un seul constructeur :

Public Sub New()
crée un objet ComboBox vide

Les éléments du ComboBox sont disponibles dans la propriété Items :

Public ReadOnly Property Items As ComboBox.ObjectCollection

C'est une propriété indexée, Items(i) désignant l'élément i du Combo. Soit C un combo et C.Items sa liste d'éléments. On a les propriétés suivantes :

 
nombre d'éléments du combo
C.Items(i)
élément i du combo
C.Add(object o)
ajoute l'objet o en dernier élement du combo
C.AddRange(object() objets)
ajoute un tableau d'objets en fin de combo
C.Insert(int i, object o)
ajoute l'objet o en position i du combo
C.RemoveAt(int i)
enlève l'élément i du combo
C.Remove(object o)
enlève l'objet o du combo
C.Clear()
supprime tous les éléments du combo
C.IndexOf(object o)
rend la position i de l'objet o dans le combo

On peut s'étonner qu'un combo puisse contenir des objets alors qu'habituellement il contient des chaînes de caractères. Au niveau visuel, ce sera le cas. Si un ComboBox contient un objet obj, il affiche la chaîne obj.ToString(). On se rappelle que tout objet à une méthode ToString héritée de la classe object et qui rend une chaîne de caractères "représentative" de l'objet. L'élément sélectionné dans le combo C est C.SelectedItem ou C.Items(C.SelectedIndex)C.SelectedIndex est le n° de l'élément sélectionné, ce n° partant de zéro pour le premier élément.

Lors du choix d'un élément dans la liste déroulante se produit l'événement SelectedIndexChanged qui peut être alors utilisé pour être averti du changement de sélection dans le combo. Dans l'application suivante, nous utilisons cet événement pour afficher l'élément qui a été sélectionné dans la liste.

Image

Nous ne présentons que le code pertinent de la fenêtre. Dans le constructeur du formulaire nous remplissons le combo :


  Public Sub New()
    ' création formulaire
    InitializeComponent()
    ' remplissage combo
    cmbNombres.Items.AddRange(New String() {"zéro", "un", "deux", "trois", "quatre"})
        ' nous sélectionnons le 1er élément de la liste
    cmbNombres.SelectedIndex = 0
  End Sub

Nous traitons l'événement SelectedIndexChanged du combo qui signale un nouvel élément sélectionné :


    Private Sub cmbNombres_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmbNombres.SelectedIndexChanged
        ' l'élément sélectionné a changé - on l'affiche
        MessageBox.Show("Elément sélectionné : (" & cmbNombres.SelectedItem.ToString & "," & cmbNombres.SelectedIndex & ")", "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

5.4.4. composant ListBox

On se propose de construire l'interface suivante :

Les composants de cette fenêtre sont les suivants :

type
nom
rôle/propriétés
0
Form
Form1
formulaire - BorderStyle=FixedSingle
1
TextBox
txtSaisie
champ de saisie
2
Button
btnAjouter
bouton permettant d'ajouter le contenu du champ de saisie 1 dans la liste 3
3
ListBox
listBox1
liste 1
4
ListBox
listBox2
liste 2
5
Button
btn1TO2
transfère les éléments sélectionnés de liste 1 vers liste 2
6
Button
cmd2T0
fait l'inverse
7
Button
btnEffacer1
vide la liste 1
8
Button
btnEffacer2
vide la liste 2
  • L'utilisateur tape du texte dans le champ 1. Il l'ajoute à la liste 1 avec le bouton Ajouter (2). Le champ de saisie (1) est alors vidé et l'utilisateur peut ajouter un nouvel élément.
  • Il peut transférer des éléments d'une liste à l'autre en sélectionnant l'élément à transférer dans l'une des listes et en choississant le bouton de transfert adéquat 5 ou 6. L'élément transféré est ajouté à la fin de la liste de destination et enlevé de la liste source.
  • Il peut double-cliquer sur un élément de la liste 1. Ce élément est alors transféré dans la boîte de saisie pour modification et enlevé de la liste 1.

Les boutons sont allumés ou éteints selon les règles suivantes :

  • le bouton Ajouter n'est allumé que s'il y a un texte non vide dans le champ de saisie
  • le bouton 5 de transfert de la liste 1 vers la liste 2 n'est allumé que s'il y a un élément sélectionné dans la liste 1
  • le bouton 6 de transfert de la liste 2 vers la liste 1 n'est allumé que s'il y a un élément sélectionné dans la liste 2
  • les boutons 7 et 8 d'effacement des listes 1 et 2 ne sont allumés que si la liste à effacer contient des éléments.

Dans les conditions précédentes, tous les boutons doivent être éteints lors du démarrage de l'application. C'est la propriété Enabled des boutons qu'il faut alors positionner à false. On peut le faire au moment de la conception ce qui aura pour effet de générer le code correspondant dans la méthode InitializeComponent ou de le faire nous-mêmes dans le constructeur comme ci-dessous :


    Public Sub New()
        ' création initiale du formulaire
        InitializeComponent()
        ' initialisations complémentaires
        ' on inhibe un certain nombre de boutons
        btnAjouter.Enabled = False
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
        btnEffacer1.Enabled = False
        btnEffacer2.Enabled = False
    End Sub

L'état du bouton Ajouter est contrôlé par le contenu du champ de saisie. C'est l'événement TextChanged qui nous permet de suivre les changements de ce contenu :


    ' chgt dans champ txtsaisie
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' le contenu de txtSaisie a changé
        ' le bouton Ajouter n'est allumé que si la saisie est non vide
        btnAjouter.Enabled = txtSaisie.Text.Trim() <> ""
    End Sub

L'état des boutons de transfert dépend du fait qu'un élément a été sélectionné ou non dans la liste qu'ils contrôlent :


    ' chgt de l'élément sélectionné sans listbox1
    Private Sub listBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox1.SelectedIndexChanged

        ' un élément a été sélectionné
        ' on allume le bouton de transfert 1 vers 2
        btn1TO2.Enabled = True
    End Sub

    ' chgt de l'élément sélectionné sans listbox2
    Private Sub listBox2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox2.SelectedIndexChanged
        ' un élément a été sélectionné
        ' on allume le bouton de transfert 2 vers 1
        btn2TO1.Enabled = True
    End Sub

Le code associé au clic sur le bouton Ajouter est le suivant :


    ' clic sur btn Ajouter
    Private Sub btnAjouter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' ajout d'un nouvel élément  la liste 1
        listBox1.Items.Add(txtSaisie.Text.Trim())
        ' raz de la saisie
        txtSaisie.Text = ""
        ' Liste 1 n'est pas vide
        btnEffacer1.Enabled = True
        ' retour du focus sur la boîte de saisie
        txtSaisie.Focus()
    End Sub

On notera la méthode Focus qui permet de mettre le "focus" sur un contrôle du formulaire. Le code associé au clic sur les boutons Effacer :


    ' clic sur btn Effacer1
    Private Sub btnEffacer1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer1.Click
        ' on efface la liste 1
        listBox1.Items.Clear()
        btnEffacer1.Enabled = False
    End Sub

    ' clic sur btn effacer2
    Private Sub btnEffacer2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' on efface la liste 2
        listBox2.Items.Clear()
        btnEffacer2.Enabled = False
    End Sub

Le code de transfert des éléments sélectionnés d'une liste vers l'autre :


    ' clic sur btn btn1to2
    Private Sub btn1TO2_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn1TO2.Click
        ' transfert de l'élément sélectionné dans Liste 1 vers Liste 2
        transfert(listBox1, listBox2)
        ' boutons Effacer
        btnEffacer2.Enabled = True
        btnEffacer1.Enabled = listBox1.Items.Count <> 0
        ' boutons de transfert
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' clic sur btn btn2to1
    Private Sub btn2TO1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn2TO1.Click
        ' transfert de l'élément slectionné dans Liste 2 vers Liste 1
        transfert(listBox2, listBox1)
        ' boutons Effacer
        btnEffacer1.Enabled = True
        btnEffacer2.Enabled = listBox2.Items.Count <> 0
        ' boutons de transfert
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' transfert
    Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
        ' transfert de l'élément sélectionné de la liste 1 vers la liste l2
        ' un élément sélectionné ?
        If l1.SelectedIndex = -1 Then Return
        ' ajout dans l2
        l2.Items.Add(l1.SelectedItem)
        ' suppression dans l1
        l1.Items.RemoveAt(l1.SelectedIndex)
    End Sub

Tout d'abord, on crée une méthode


    Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)

qui transfère dans la liste l2 l'élément sélectionné dans la liste l1. Cela nous permet d'avoir une seule méthode au lieu de deux pour transférer un élément de listBox1 vers listBox2 ou de listBox2 vers listBox1. Avat de faire le transfert, on s'assure qu'il y a bien un élément sélectionné dans la liste l1 :


        ' un élément sélectionné ?
        If l1.SelectedIndex = -1 Then Return

La propriété SelectedIndex vaut -1 si aucun élément n'est actuellement sélectionné. Dans les procédures


    Private Sub btnXTOY_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnXTOY.Click

on opère le transfert de la liste X vers la liste Y et on change l'état de certains boutons pour refléter le nouvel état des listes.

5.4.5. cases à cocher CheckBox, boutons radio ButtonRadio

Nous nous proposons d'écrire l'application suivante :

Les composants de la fenêtre sont les suivants :

type
nom
rôle
1
RadioButton
radioButton1
radioButton2
radioButton3
3 boutons radio
2
CheckBox
chechBox1
chechBox2
chechBox3
3 cases à cocher
3
ListBox
lstValeurs
une liste

Si on construit les trois boutons radio l'un après l'autre, ils font partie par défaut d'un même groupe. Aussi lorsque l'un est coché, les autres ne le sont pas. L'événement qui nous intéresse pour ces six contrôles est l'événement CheckChanged indiquant que l'état de la case à cocher ou du bouton radio a changé. Cet état est représenté dans les deux cas par la propriété booléenne Check qui à vrai signifie que le contrôle est coché. Nous avons ici utilisé une seule méthode pour traiter les six événements CheckChanged, la méthode affiche :


  ' affiche
    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
        ' affiche l'état du bouton radio ou de la case à cocher
        ' est-ce un checkbox ?
        If (TypeOf (sender) Is CheckBox) Then
            Dim chk As CheckBox = CType(sender, CheckBox)
            lstValeurs.Items.Insert(0, chk.Name & "=" & chk.Checked)
        End If
        ' est-ce un radiobutton ?
        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 syntaxe TypeOf (sender) Is CheckBox permet de vérifier si l'objet sender est de type CheckBox. Cela nous permet ensuite de faire un transtypage vers le type exact de sender. La méthode affiche écrit dans la liste lstValeurs le nom du composant à l'origine de l'événement et la valeur de sa propriété Checked. A l'exécution, on voit qu'un clic sur un bouton radio provoque deux événements CheckChanged : l'un sur l'ancien bouton coché qui passe à "non coché" et l'autre sur le nouveau bouton qui passe à "coché".

5.4.6. variateurs ScrollBar

Il existe plusieurs types de variateur : le variateur horizontal (hScrollBar),
le variateur vertical (vScrollBar), l'incrémenteur (NumericUpDown).

Réalisons l'application suivante :

type
nom
rôle
1
hScrollBar
hScrollBar1
un variateur horizontal
2
hScrollBar
hScrollBar2
un variateur horizontal qui suit les variations du variateur 1
3
TextBox
txtValeur
affiche la valeur du variateur horizontal
ReadOnly=true pour empêcher toute saisie
4
NumericUpDown
incrémenteur
permet de fixer la valeur du variateur 2
  • Un variateur ScrollBar permet à l'utilisateur de choisir une valeur dans une plage de valeurs entières symbolisée par la "bande" du variateur sur laquelle se déplace un curseur. La valeur du variateur est disponible dans sa propriété Value.
  • Pour un variateur horizontal, l'extrémité gauche représente la valeur minimale de la plage, l'extrémité droite la valeur maximale, le curseur la valeur actuelle choisie. Pour un variateur vertical, le minimum est représenté par l'extrémité haute, le maximum par l'extrémité basse. Ces valeurs sont représentées par les propriétés Minimum et Maximum et valent par défaut 0 et 100.
  • Un clic sur les extrémités du variateur fait varier la valeur d'un incrément (positif ou négatif) selon l'extrémité cliquée appelée SmallChange qui est par défaut 1.
  • Un clic de part et d'autre du curseur fait varier la valeur d'un incrément (positif ou négatif) selon l'extrémité cliquée appelée LargeChange qui est par défaut 10.
  • Lorsqu'on clique sur l'extrémité supérieure d'un variateur vertical, sa valeur diminue. Cela peut surprendre l'utilisateur moyen qui s'attend normalement à voir la valeur "monter". On règle ce problème en donnant une valeur négative aux propriétés SmallChange et LargeChange
  • Ces cinq propriétés (Value, Minimum, Maximum, SmallChange, LargeChange) sont accessibles en lecture et écriture.
  • L'événement principal du variateur est celui qui signale un changement de valeur : l'événement Scroll.

Un composant NumericUpDown est proche du variateur : il a lui aussi les propriétés Minimum, Maximum et Value, par défaut 0, 100, 0. Mais ici, la propriété Value est affichée dans une boîte de saisie faisant partie intégrante du contrôle. L'utilisateur peut lui même modifier cette valeur sauf si on a mis la propriété ReadOnly du contrôle à vrai. La valeur de l'incrément est fixé par la propriété Increment, par défaut 1. L'événement principal du composant NumericUpDown est celui qui signale un changement de valeur : l'événement ValueChanged. Le code utile de notre application est le suivant :

Le formulaire est mis en forme lors de sa construction :


    ' constructeur
    Public Sub New()
        ' création initiale du formulaire
        InitializeComponent()
        ' on donne au variateur 2 les mêmes caractéristiques qu'au variateur 1
        hScrollBar2.Minimum = hScrollBar1.Value
        hScrollBar2.Minimum = hScrollBar1.Minimum
        hScrollBar2.Maximum = hScrollBar1.Maximum
        hScrollBar2.LargeChange = hScrollBar1.LargeChange
        hScrollBar2.SmallChange = hScrollBar1.SmallChange
        ' idem pour l'incrémenteur
        incrémenteur.Minimum = hScrollBar1.Value
        incrémenteur.Minimum = hScrollBar1.Minimum
        incrémenteur.Maximum = hScrollBar1.Maximum
        incrémenteur.Increment = hScrollBar1.SmallChange
        ' on donne au TextBox la valeur du variateur 1
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

Le gestionnaire qui suit les variations de valeur du variateur 1 :


    ' gestion variateur hscrollbar1
    Private Sub hScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar1.Scroll
        ' changement de valeur du variateur 1
        ' on répercute sa valeur sur le variateur 2 et sur le textbox TxtValeur
        hScrollBar2.Value = hScrollBar1.Value
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

Le gestionnaire qui suit les variations de valeur du variateur 2 :


    ' gestion variateur hscrollbar2
    Private Sub hScrollBar2_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar2.Scroll
        ' on inhibe tout changement du variateur 2
        ' en le forçant à garder la valeur du variateur 1
        e.NewValue = hScrollBar1.Value
    End Sub

Le gestionnaire qui suit les variations du contrôle incrémenteur :


    ' gestion incrémenteur
    Private Sub incrémenteur_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles incrémenteur.ValueChanged
        ' on fixe la valeur du variateur 2
        hScrollBar2.Value = CType(incrémenteur.Value, Integer)
    End Sub

5.5. Événements souris

Lorsqu'on dessine dans un conteneur, il est important de connaître la position de la souris pour par exemple afficher un point lors d'un clic. Les déplacements de la souris provoquent des événements dans le conteneur dans lequel elle se déplace.

 
la souris vient d'entrer dans le domaine du contrôle
MouseLeave
la souris vient de quitter le domaine du contrôle
MouseMove
la souris bouge dans le domaine du contrôle
MouseDown
Pression sur le bouton gauche de la souris
MouseUp
Relâchement du bouton gauche de la souris
DragDrop
l'utilisateur lâche un objet sur le contrôle
DragEnter
l'utilisateur entre dans le domaine du contrôle en tirant un objet
DragLeave
l'utilisateur sort du domaine du contrôle en tirant un objet
DragOver
l'utilisateur passe au-dessus domaine du contrôle en tirant un objet

Voici un programme permettant de mieux appréhender à quels moments se produisent les différents événements souris :

type
nom
rôle
1
Label
lblPosition
pour afficher la position de la souris dans le formulaire 1, la liste 2 ou le bouton 3
2
ListBox
lstEvts
pour afficher les évts souris autres que MouseMove
3
Button
btnEffacer
pour effacer le contenu de 2

Les gestionnaires d'événements sont les suivants. Pour suivre les déplacements de la souris sur les trois contrôles, on n'écrit qu'un seul gestionnaire :


    ' évt 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
        ' mvt souris - on affiche les coordonnes (X,Y) de celle-ci
        lblPosition.Text = "(" & e.X & "," & e.Y & ")"
    End Sub

Il faut savoir qu'à chaque fois que la souris entre dans le domaine d'un contrôle, son système de coordonnées change. Son origine (0,0) est le coin supérieur gauche du contrôle sur lequel elle se trouve. Ainsi à l'exécution, lorsqu'on passe la souris du formulaire au bouton, on voit clairement le changement de coordonnées. Afin de mieux voir ces changements de domaine de la souris, on peut utiliser la propriété Cursor des contrôles :

Image

Cette propriété permet de fixer la forme du curseur de souris lorsque celle-ci entre dans le domaine du contrôle. Ainsi dans notre exemple, nous avons fixé le curseur à Default pour le formulaire lui-même, Hand pour la liste 2 et à No pour le bouton 3 comme le montrent les copies d'écran ci-dessous.

Image

Image

Image

Dans la méthode [InitializeComponent], le code généré par ces choix est le suivant :


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

Par ailleurs, pour détecter les entrées et sorties de la souris sur la liste 2, nous traitons les événements MouseEnter et MouseLeave de cette même liste :


    ' 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

    ' evt 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
    
' affiche
    Private Sub affiche(ByVal message As String)
        ' on affiche le message en haut de la liste des evts
        lstEvts.Items.Insert(0, message)
    End Sub

Image

Pour traiter les clics sur le formulaire, nous traitons les événements MouseDown et MouseUp :


    ' évt 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

    ' évt 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

Enfin, le code du gestionnaire de clic sur le bouton Effacer :


    ' évt btnEffacer_Click
    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' efface la liste des evts
        lstEvts.Items.Clear()
    End Sub

5.6. Créer une fenêtre avec menu

Voyons maintenant comment créer une fenêtre avec menu. Nous allons créer la fenêtre suivante :

Le contrôle 1 est un TextBox en lecture seule (ReadOnly=true) et de nom txtStatut. L'arborescence du menu est la suivante :

Les options de menu sont des contrôles comme les autres composants visuels et ont des propriétés et événements. Par exemple le tableau des propriétés de l'option de menu A1 :

Image

Deux propriétés sont utilisées dans notre exemple :

 
le nom du contrôle menu
Text
le libellé de l'option de menu

Les propriétés des différentes options de menu de notre exemple sont les suivantes :

Name
Text
mnuA
options A
mnuA1
A1
mnuA2
A2
mnuA3
A3
mnuB
options B
mnuB1
B1
mnuSep1
- (séparateur)
mnuB2
B2
mnuB3
B3
mnuB31
B31
mnuB32
B32

Pour créer un menu, on choisit le composant "MainMenu" dans la barre "ToolBox" :

Image

On a alors un menu vide qui s'installe sur le formulaire avec des cases vides intitulées "Type Here". Il suffit d'y indiquer les différentes options du menu :

Image

Pour insérer un séparateur entre deux options comme ci-dessus entre les options B1 et B2, positionnez-vous à l'emplacement du séparateur dans le menu, cliquez droit et prenez l'option Insert Separator :

Image

Si on lance l'application par ctrl-F5, on obtient un formulaire avec un menu qui pour l'instant ne fait rien. Les options de menu sont traitées comme des composants : elles ont des propriétés et des événements. Dans [fenêtre de code], sélectionnez le composant mnuA1 puis sélectionnez les événements associés :

Image

Si ci-dessus on génère l'événement Click, VS.NET génère automatiquement la procédure suivante :


Private Sub mnuA1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuA.Click
....
    End Sub

Nous pourrions procéder ainsi pour toutes les options de menu. Ici, la même procédure peut être utilisée pour toutes les options. Aussi renomme-t-on la procédure précédente affiche et nous déclarons les événements qu'elle gère :


    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
        ' affiche dans le TextBox le nom du sous-menu choisi
        txtStatut.Text = (CType(sender, MenuItem)).Text
    End Sub

Dans cette méthode, nous nous contentons d'afficher la propriété Text de l'option de menu à la source de l'événement. La source de l'événement sender est de type object. Les options de menu sont elles de type MenuItem, aussi est-on obligé ici de faire un transtypage de object vers MenuItem. Exécutez l'application et sélectionnez l'option A1 pour obtenir le message suivant :

Image

Le code utile de cette application, outre celui de la méthode affiche, est celui de la construction du menu dans le constructeur du formulaire (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

On notera l'instruction qui associe le menu au formulaire :


    Me.Menu = Me.mainMenu1

5.7. Composants non visuels

Nous nous intéressons maintenant à un certain nombre de composants non visuels : on les utilise lors de la conception mais on ne les voit pas lors de l'exécution.

5.7.1. Boîtes de dialogue OpenFileDialog et SaveFileDialog

Nous allons construire l'application suivante :

Les contrôles sont les suivants :

type
nom
rôle
1
TextBox multilignes
txtTexte
texte tapé par l'utilisateur ou chargé à partir d'un fichier
2
Button
btnSauvegarder
permet de sauvegarder le texte de 1 dans un fichier texte
3
Button
btnCharger
permet de charger le contenu d'un fichier texte dans 1
4
Button
btnEffacer
efface le contenu de 1

Deux contrôles non visuels sont utilisés :

Image

Lorsqu'ils sont pris dans le "ToolBox " et déposés sur le formulaire, ils sont placés dans une zone à part en bas du formulaire. Les composants "Dialog" sont pris dans le "ToolBox" :

Image

Le code du bouton Effacer est simple :


    Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' on efface la boîte de saisie
        txtTexte.Text = ""
    End Sub

La classe SaveFileDialog est définie comme suit :

Image

Elle dérive de plusieurs niveaux de classe. De ces nombreuses propriétés et méthodes nous retiendrons les suivantes :

string Filter
les types de fichiers proposés dans la liste déroulante des types de fichiers de la boîte de dialogue
int FilterIndex
le n° du type de fichier proposé par défaut dans la liste ci-dessus. Commence à 0.
String
InitialDirectory
le dossier présenté initialement pour la sauvegarde du fichier
string FileName
le nom du fichier de sauvegarde indiqué par l'utilisateur
DialogResult.ShowDialog()
méthode qui affiche la boîte de dialogue de sauvegarde. Rend un résultat de type DialogResult.

La méthode ShowDialog affiche une boîte de dialogue analogue à la suivante :

1
liste déroulante construite à partir de la propriété Filter. Le type de fichier proposé par défaut est fixé par FilterIndex
2
dossier courant, fixé par InitialDirectory si cette propriété a été renseignée
3
nom du fichier choisi ou tapé directement par l'utilisateur. Sera disponible dans la propriété FileName
4
boutons Enregistrer/Annuler. Si le bouton Enregistrer est utilisé, la fonction ShowDialog rend le résultat DialogResult.OK

La procédure de sauvegarde peut s'écrire ainsi :


    Private Sub btnSauvegarder_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnSauvegarder.Click
        ' on sauvegarde la boîte de saisie dans un fichier texte
        ' on paramètre la boîte de dialogue savefileDialog1
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        saveFileDialog1.FilterIndex = 0
        ' on affiche la boîte de dialogue et on récupère son résultat
        If saveFileDialog1.ShowDialog() = DialogResult.OK Then
            ' on récupère le nom du fichier
            Dim nomFichier As String = saveFileDialog1.FileName
            Dim fichier As StreamWriter = Nothing
            Try
                ' on ouvre le fichier en écriture
                fichier = New StreamWriter(nomFichier)
                ' on écrit le texte dedans
                fichier.Write(txtTexte.Text)
            Catch ex As Exception
                ' problème
                MessageBox.Show("Problème à l'écriture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' on ferme le fichier
                Try
                    fichier.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • On fixe le dossier initial au dossier qui contient l'exécutable de l'application :
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • On fixe les types de fichiers à présenter
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"

On notera la syntaxe des filtres filtre1|filtre2|..|filtren avec filtrei= Texte|modèle de fichier. Ici l'utilisateur aura le choix entre les fichiers *.txt et *.*.

  • On fixe le type de fichier à présenter au début
        saveFileDialog1.FilterIndex = 0

Ici, ce sont les fichiers de type *.txt qui seront présentés tout d'abord à l'utilisateur.

  • La boîte de dialogue est affichée et son résultat récupéré

    If saveFileDialog1.ShowDialog() = DialogResult.OK Then
  • Pendant que la boîte de dialogue est affichée, l'utilisateur n'a plus accès au formulaire principal (boîte de dialogue dite modale). L'utilisateur fixe le nom du fichier à sauvegarder et quitte la boîte soit par le bouton Enregistrer, soit par le bouton Annuler soit en fermant la boîte. Le résultat de la méthode ShowDialog est DialogResult.OK uniquement si l'utilisateur a utilisé le bouton Enregistrer pour quitter la boîte de dialogue.
  • Ceci fait, le nom du fichier à créer est maintenant dans la propriété FileName de l'objet saveFileDialog1. On est alors ramené à la création classique d'un fichier texte. On y écrit le contenu du TextBox : txtTexte.Text tout en gérant les exceptions qui peuvent se produire.

La classe OpenFileDialog est très proche de la classe SaveFileDialog et dérive de la même lignée de classes. De ces propriétés et méthodes nous retiendrons les suivantes :

string Filter
les types de fichiers proposés dans la liste déroulante des types de fichiers de la boîte de dialogue
int FilterIndex
le n° du type de fichier proposé par défaut dans la liste ci-dessus. Commence à 0.
string InitialDirectory
le dossier présenté initialement pour la recherche du fichier à ouvrir
string FileName
le nom du fichier à ouvrir indiqué par l'utilisateur
DialogResult.ShowDialog()
méthode qui affiche la boîte de dialogue de sauvegarde. Rend un résultat de type DialogResult.

La méthode ShowDialog affiche une boîte de dialogue analogue à la suivante :

1
liste déroulante construite à partir de la propriété Filter. Le type de fichier proposé par défaut est fixé par FilterIndex
2
dossier courant, fixé par InitialDirectory si cette propriété a été renseignée
3
nom du fichier choisi ou tapé directement par l'utilisateur. Sera disponible dans la proprité FileName
4
boutons Ouvrir/Annuler. Si le bouton Ouvrir est utilisé, la fonction ShowDialog rend le résultat DialogResult.OK

La procédure d'ouverture peut s'écrire ainsi :


Private Sub btnCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCharger.Click
        ' on charge un fichier texte dans la boîte de saisie
        ' on paramètre la boîte de dialogue openfileDialog1
        openFileDialog1.InitialDirectory = Application.ExecutablePath
        openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        openFileDialog1.FilterIndex = 0
        ' on affiche la boîte de dialogue et on récupère son résultat
        If openFileDialog1.ShowDialog() = DialogResult.OK Then
            ' on récupère le nom du fichier
            Dim nomFichier As String = openFileDialog1.FileName
            Dim fichier As StreamReader = Nothing
            Try
                ' on ouvre le fichier en lecture
                fichier = New StreamReader(nomFichier)
                ' on lit tout le fichier et on le met dans le TextBox
                txtTexte.Text = fichier.ReadToEnd()
            Catch ex As Exception
                ' problème
                MessageBox.Show("Problème à la lecture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' on ferme le fichier
                Try
                    fichier.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • On fixe le dossier initial au dossier qui contient l'exécutable de l'application :
            saveFileDialog1.InitialDirectory=Application.ExecutablePath
  • On fixe les types de fichiers à présenter
            saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
  • On fixe le type de fichier à présenter au début
            saveFileDialog1.FilterIndex = 0

Ici, ce sont les fichiers de type *.txt qui seront présentés tout d'abord à l'utilisateur.

  • La boîte de dialogue est affichée et son résultat récupéré

    If openFileDialog1.ShowDialog() = DialogResult.OK Then

Pendant que la boîte de dialogue est affichée, l'utilisateur n'a plus accès au formulaire principal (boîte de dialogue dite modale). L'utilisateur fixe le nom du fichier à ouvrir et quitte la boîte soit par le bouton Ouvrir, soit par le bouton Annuler soit en fermant la boîte. Le résultat de la méthode ShowDialog est DialogResult.OK uniquement si l'utilisateur a utilisé le bouton Ouvrir pour quitter la boîte de dialogue.

  • Ceci fait, le nom du fichier à créer est maintenant dans la propriété FileName de l'objet openFileDialog1. On est alors ramené à la lecture classique d'un fichier texte. On notera la méthode qui permet de lire la totalité d'un fichier :
                    txtTexte.Text=fichier.ReadToEnd
  • le contenu du fichier est mis dans le TextBox txtTexte. On gère les exceptions qui peuvent se produire.

5.7.2. Boîtes de dialogue FontColor et ColorDialog

Nous continuons l'exemple précédent en présentant deux nouveaux boutons :

type
nom
rôle
6
Button
btnCouleur
pour fixer la couleur des caractères du TextBox
7
Button
btnPolice
pour fixer la police de caractères du TextBox

Nous déposons sur le formulaire un contrôle ColorDialog et un contrôle FontDialog :

Image

Les classes FontDialog et ColorDialog ont une méthode ShowDialog analogue à la méthode ShowDialog des classes OpenFileDialog et SaveFileDialog. La méthode ShowDialog de la classe ColorDialog permet de choisir une couleur :

Image

Si l'utilisateur quitte la boîte de dialogue avec le bouton OK, le résultat de la méthode ShowDialog est DialogResult.OK et la couleur choisie est dans la propriété Color de l'objet ColorDialog utilisé. La méthode ShowDialog de la classe FontDialog permet de choisir une police de caractères :

Image

Si l'utilisateur quitte la boîte de dialogue avec le bouton OK, le résultat de la méthode ShowDialog est DialogResult.OK et la police choisie est dans la propriété Font de l'objet FontDialog utilisé. Nous avons les éléments pour traiter les clics sur les boutons Couleur et Police :


    Private Sub btnCouleur_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCouleur.Click
        ' choix d'une couleur de texte
        If colorDialog1.ShowDialog() = DialogResult.OK Then
            ' on change la propriété forecolor du 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
        ' choix d'une police de caractères
        If fontDialog1.ShowDialog() = DialogResult.OK Then
            ' on change la propriété font du TextBox
            txtTexte.Font = fontDialog1.Font
        End If
    End Sub

5.7.3. Timer

Nous nous proposons ici d'écrire l'application suivante :

Type
Nom
Rôle
1
TextBox, ReadOnly=true
txtChrono
affiche un chronomètre
2
Button
btnArretMarche
bouton Arrêt/Marche du chronomètre
3
Timer
timer1
composant émettant ici un événement toutes les secondes

Le chronomètre en marche :

Image

Le chronomètre arrêté :

Image

Pour changer toutes les secondes le contenu du TextBox txtChrono, il nous faut un composant qui génère un événement toutes les secondes, événement qu'on pourra intercepter pour mettre à jour l'affichage du chronomètre. Ce composant c'est le Timer :

Image

Une fois ce composant installé sur le formulaire (dans la partie des composants non visuels), un objet de type Timer est créé dans le constructeur du formulaire. La classe System.Windows.Forms.Timer est définie comme suit :

Image

De ses propriétés nous ne retiendrons que les suivantes :

Interval
nombre de millisecondes au bout duquel un événement Tick est émis.
Tick
l'événement produit à la fin de Interval millisecondes
Enabled
rend le timer actif (true) ou inactif (false)

Dans notre exemple le timer s'appelle timer1 et timer1.Interval est mis à 1000 ms (1s). L'événement Tick se produira donc toutes les secondes. Le clic sur le bouton Arrêt/Marche est traité par la procédure suivante :


    Private Sub btnArretMarche_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnArretMarche.Click
        ' arrêt ou marche ?
        If btnArretMarche.Text = "Marche" Then
            ' on note l'heure de début
            début = DateTime.Now
            ' on l'affiche
            txtChrono.Text = "00:00:00"
            ' on lance le timer
            timer1.Enabled = True
            ' on change le libellé du bouton
            btnArretMarche.Text = "Arrêt"
            ' fin
            Return
        End If        '
        If btnArretMarche.Text = "Arrêt" Then
            ' arrêt du timer
            timer1.Enabled = False
            ' on change le libellé du bouton
            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
        ' une seconde s'est écoulée
        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

Le libellé du bouton Arret/Marche est soit "Arrêt" soit "Marche". On est donc obligé de faire un test sur ce libellé pour savoir quoi faire.

  • dans le cas de "Marche", on note l'heure de début dans une variable qui est une variable globale de l'objet formulaire, le timer est lancé (Enabled=true) et le libellé du bouton passe à "Arrêt".
  • dans le cas de "Arrêt", on arrête le timer (Enabled=false) et on passe le libellé du bouton à "Marche".

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 d'instance
  Private début As DateTime

L'attribut début ci-dessus est connu dans toutes les méthodes de la classe. Il nous reste à traiter l'événement Tick sur l'objet timer1, événement qui se produit toutes les secondes :


    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' une seconde s'est écoulée
        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

On calcule le temps écoulé depuis l'heure de lancement du chronomètre. On obtient un objet de type TimeSpan qui représente une durée dans le temps. Celle-ci doit être affichée dans le chronomètre sous la forme hh:mm:ss. Pour cela nous utilisons les propriétés Hours, Minutes, Seconds de l'objet TimeSPan qui représentent respectivement les heures, minutes, secondes de la durée que nous affichons au format ToString("d2") pour avoir un affichage sur 2 chiffres.

5.8. L'exemple IMPOTS

On reprend l'application IMPOTS déjà traitée deux fois. Nous y ajoutons maintenant une interface graphique :

Les contrôles sont les suivants

type
nom
rôle
1
RadioButton
rdOui
coché si marié
2
RadioButton
rdNon
coché si pas marié
3
NumericUpDown
incEnfants
nombre d'enfants du contribuable
Minimum=0, Maximum=20, Increment=1
4
TextBox
txtSalaire
salaire annuel du contribuable en F
5
TextBox
txtImpots
montant de l'impôt à payer
ReadOnly=true
6
Button
btnCalculer
lance le calcul de l'impôt
7
Button
btnEffacer
remet le formulaire dans son état initial lors du chargement
8
Button
btnQuitter
pour quitter l'application

Règles de fonctionnement

  • le bouton Calculer reste éteint tant qu'il n'y a rien dans le champ du salaire
  • si lorsque le calcul est lancé, il s'avère que le salaire est incorrect, l'erreur est signalée :

Image

Le programme est donné ci-dessous. Il utilise la classe impot créée dans le chapitre sur les classes. Une partie du code produit automatiquement pas VS.NET n'a pas été ici reproduit.


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
' options
Option Explicit On 
Option Strict On

' espaces de noms
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data

' classe formulaire
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

  ' tableaux de données nécessaires au calcul de l'impôt
  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}
  ' objet impôt
  Private objImpôt As impot = Nothing


  Public Sub New()
    InitializeComponent()
    ' initialisation du formulaire
    btnEffacer_Click(Nothing, Nothing)
    btnCalculer.Enabled = False
    ' création d'un objet impôt
    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)
      ' on inhibe le champ de saisie du salaire
      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
    ' raz du formulaire
    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
    ' état du bouton Calculer
    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 application
    Application.Exit()
  End Sub 'btnQuitter_Click


  Private Sub btnCalculer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnCalculer.Click
    ' le salaire est-il correct ?
    Dim intSalaire As Integer = 0
    Try
      ' récupération du salaire
      intSalaire = Integer.Parse(txtSalaire.Text)
      ' il doit être >=0
      If intSalaire < 0 Then
        Throw New Exception("")
      End If
    Catch ex As Exception
      ' msg d'erreur
      MessageBox.Show(Me, "Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error)
      ' focus sur champ erroné
      txtSalaire.Focus()
      ' sélection du texte du champ de saisie
      txtSalaire.SelectAll()
      ' retour à l'interface visuelle
      Return
    End Try 'try-catch
    ' le salaire est correct - on calcule l'impôt
    txtImpots.Text = "" & CLng(objImpôt.calculer(rdOui.Checked, CInt(incEnfants.Value), intSalaire))
  End Sub 'btnCalculer_Click
End Class

Nous utilisons ici l'assemblage impots.dll résultat de la compilation de la classe impots du chapitre 2. Rappelons que cet assemblage peut être produit en mode console par la commande

dos>vbc /t:library impots.vb

Cette commande produit le fichier impots.dll appelé assemblage. Cet assemblage peut être ensuite utilisé dans différents projets. Ici, dans notre projet sous VS.NET, nous utilisons la fenêtre des propriétés du projet :

Image

Pour ajouter une référence (un assemblage) nous cliquons droit sur le lot clé References ci-dessus, nous prenons l'option [Ajouter une référence] et nous désignons l'assemblage [impots.dll] que nous avons pris soin de mettre dans le dossier du projet :

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

Une fois inclus l'assemblage [impots.dll] dans le projet, la classe [impots] devient connue du projet. Auparavant elle ne l'est pas. Une autre méthode est d'inclure le source impots.vb dans le projet. Pour cela, dans la fenêtre des propriétés du projet, on clique droit sur le projet et on prend l'option [Ajouter/Ajouter un élément existant] et on désigne le fichier impots.vb.