Share via


Retrouvez vos vieux réflexes Visual Basic 6.0

Billy Hollis

Résumé : Billy Hollis vous explique comment créer des tableaux de contrôles, des collections Forms et une collection Controls réunissant tous les contrôles sur un formulaire dans Visual Basic .NET.

Je connais Microsoft Visual Basic® 6.0 quasiment sur le bout des doigts. Visual Basic .NET semble toutefois beaucoup mieux correspondre à mon idée d'un logiciel. J'ai en effet acquis, à présent, suffisamment de flexibilité pour faire des choses que je n'aurais jamais pu envisager avant, grâce à des fonctionnalités entièrement orientées objet, au lieu des fonctionnalités partielles proposées par Visual Basic 6.0 ; sans compter l'amélioration du déploiement, les interfaces Web, ainsi que la suppression de ces COM un peu bizarres...

Au cours de mes conversations avec des développeurs, certains ont néanmoins évoqué avec nostalgie quelques fonctionnalités de Visual Basic 6.0 inexistantes dans Visual Basic .NET. Ceci est tout à fait compréhensible. Lorsque l'on a pris l'habitude de faire les choses d'une certaine manière, il peut être particulièrement frustrant de voir ses techniques disparaître.

Toutefois, l'avantage d'un environnement flexible et entièrement orienté objet est qu'il offre généralement la possibilité de récupérer ces vieilles techniques via une utilisation intelligente des nouvelles techniques. Dans cet article, j'aborderai trois des fonctionnalités les plus importantes qui ne sont pas incluses dans Visual Basic .NET :

  1. les tableaux de contrôles ;
  2. les collections Forms ;
  3. une collection Controls réunissant tous les contrôles sur un formulaire.

Pour chacune de ces fonctionnalités, je vous indiquerai comment la répliquer dans Visual Basic .NET.

Tableaux de contrôles

Un tableau de contrôles dans Visual Basic 6.0 et dans les versions précédentes était un groupe de contrôles du même type et partageant le même nom sur un formulaire. Tandis qu'un contrôle standard pouvait être identifié par son nom uniquement, un nom et un index étaient nécessaires pour obtenir une référence à un membre du tableau de contrôles.

Tous les membres d'un tableau de contrôles partageaient des événements communs. Ainsi, quel que soit le contrôle sur lequel vous cliquiez dans le tableau, Visual Basic 6.0 routait l'événement vers un gestionnaire d'événements Click unique. L'un des arguments de l'événement était un index indiquant le contrôle spécifique qui avait provoqué le déclenchement de l'événement.

Dans Visual Basic 6.0 et dans les versions précédentes, les développeurs utilisaient les tableaux de contrôles pour trois principales raisons :

  1. ils permettaient de raccorder une routine d'événement à plusieurs contrôles ;
  2. ils permettaient d'accéder aux contrôles sous forme de collection dans une boucle « For Each » ;
  3. ils permettaient d'ajouter à la volée de nouveaux contrôles à un formulaire.

Toutes ces opérations peuvent être facilement exécutées dans Visual Basic .NET, mais la façon d'y parvenir semble parfaitement étrangère à la plupart des développeurs Visual Basic 6.0. Examinons une technique .NET équivalente pour chacune des opérations ci-dessus.

Raccordement de contrôles à des événements

Avant d'aborder les tableaux de contrôles et les événements, il nous faut d'abord expliquer les événements dans Visual Basic .NET. Ces derniers fonctionnent assez différemment dans Visual Basic .NET et dans Visual Basic 6.0. Il est possible que vous ne remarquiez pas de changement à première vue. La façon dont les événements sont créés dans l'éditeur de code semble identique en apparence.

Si un objet est associé à des événements (il est donc déclaré avec le mot clé WithEvents), il apparaît alors dans le menu déroulant à gauche de l'éditeur de code de Visual Basic .NET. Si vous le sélectionnez dans cette liste, les événements associés apparaissent alors dans le menu déroulant à droite. Si vous sélectionnez alors un événement dans ce menu déroulant, vous obtiendrez un shell de la routine d'événement.

Les différences existant entre les événements dans Visual Basic 6.0 et Visual Basic .NET n'apparaissent vraiment que lorsque vous examinez le code contenu dans cette routine de shell. Dans Visual Basic 6.0, la routine d'événement d'un contrôle ou d'une instance de classe portait un nom standard. Ce dernier était composé du nom de l'objet, d'un trait de soulignement, puis du nom de l'événement. Ainsi, l'événement Click d'une zone de texte nommée txtAge était txtAge_Click. Il se présentait sous la forme suivante :

  

' VB6 version of the click event
Private Sub txtAge_Click()

End Sub

 

Visual Basic .NET crée également un événement Click pour txtAge nommé txtAge_Click. Toutefois, ce nom sert uniquement à faciliter la lecture du code. En réalité, une routine d'événement peut porter n'importe quel nom.

Voici l'événement Click équivalent entièrement créé par Visual Basic .NET (avec de légères modifications afin de faciliter la lecture du code) :

  

' VB.NET version of the click event
Private Sub txtAge_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click

End Sub

 

Pour cette routine d'événement, ce n'est pas le nom qui raccorde la routine à l'événement. Vous pourriez renommer la routine txtAge_Click par MyFunkyEventRoutine, ça ne ferait pas la moindre différence.

C'est au contraire la clause Handles située à la fin de la déclaration de routine qui raccorde la routine à un événement. L'événement raccordé ici est l'événement Click de la zone de texte txtAge. Tant que Handles txtAge.Click termine la routine d'événement, celle-ci se déclenche toujours lorsque vous cliquez sur la zone de texte, quel que soit le nom de la routine.

Il existe deux autres possibilités intéressantes. Tout d'abord, plusieurs routines peuvent se terminer par Handles txtAge.Click. Si quelqu'un clique sur la zone de texte txtAge, toutes les routines comportant Handles txtAge.Click sont déclenchées. (Vous ne pouvez pas, par ailleurs, contrôler l'ordre dans lequel les routines d'événement sont déclenchées et vous ne devez absolument pas être dépendant de l'ordre de leur déclenchement. Nous y reviendrons plus loin dans cet article.)

Voyons enfin la technique utilisée pour router plusieurs contrôles vers le même événement. La clause Handles terminant une routine d'événement peut répertorier autant d'événements que vous le souhaitez, séparés par des virgules. Voici une légère variante de la routine d'événement ci-dessus qui déclenche des événements Click générés par trois zones de texte différentes :

  

Private Sub MultiTextBox_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click, txtHeight.Click, txtWeight.Click

End Sub

 

Notez que j'ai renommé la routine txtAge_Click par MultiTextBox_Click. C'est une méthode assez astucieuse pour que l'utilisateur se rende compte, en lisant le code, que cette routine utilise des zones de texte autres que txtAge.

En utilisant cette syntaxe dans Visual Basic .NET, vous pouvez raccorder des contrôles au même événement sans avoir besoin d'un tableau de contrôles. Cette syntaxe offre également plus de flexibilité car elle utilise l'événement Click pour tous les contrôles. Vous n'êtes pas limité aux routines d'événements qui ne traitent qu'un type de contrôle. Une routine d'événement unique peut également intercepter les événements Click de plusieurs types de contrôles différents. Si vous avez des cases à cocher nommées chkHasCar et chkCertified, vous pouvez modifier la routine ci-dessus afin d'intercepter des événements des zones de texte et des cases à cocher :

  

Private Sub MultiControl_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click, txtHeight.Click, txtWeight.Click, _
chkHasCar.Click, chkCertified.Click

End Sub

 

 

En fait, la routine peut intercepter d'autres événements en plus des événements Click, à condition que l'événement ait la même liste d'arguments. Par exemple, les événements Enter et DoubleClick ont exactement la même liste d'arguments que l'événement Click. La routine ci-dessus pourrait alors intercepter tous ces événements pour une zone de texte et une case à cocher si elle se présentait comme suit :

  

Private Sub CatchMultipleEvents(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click, txtAge.DoubleClick, _
txtAge.Enter, chkHasCar.Click, _
chkHasCar.DoubleClick, chkHasCar.Enter

End Sub

 

Comme nous pouvons le constater, au lieu de perdre des fonctionnalités en raison de la suppression des tableaux de contrôles, nous gagnons au contraire en flexibilité en ce qui concerne la gestion des événements, par rapport à Visual Basic 6.0.

Accès aux contrôles dans une boucle « For Each »

Étant donné que nous ne disposons pas de tableaux de contrôles dans Visual Basic .NET, pour réussir à effectuer une boucle dans un ensemble de contrôles, ces contrôles doivent faire partie d'une collection. La façon la plus directe de procéder est de créer une collection et d'y ajouter les contrôles appropriés. Par exemple, supposons que nous ayons un formulaire comportant trois cases à cocher nommées CheckBox1, CheckBox2 et CheckBox3. Nous pourrions déclarer un élément ArrayList dans notre formulaire et y insérer les cases à cocher, de la façon suivante :

  

Dim colMyCheckBoxes As ArrayList
Private Sub BuildCheckBoxCollection()
colMyCheckBoxes = New ArrayList
colMyCheckBoxes.Add(CheckBox1)
colMyCheckBoxes.Add(CheckBox2)
colMyCheckBoxes.Add(CheckBox3)
End Sub

 

Normalement, cette routine est exécutée dans l'événement Load du formulaire. Cet événement Load se présenterait de la façon suivante :

  

Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
BuildCheckBoxCollection()
End Sub

 

Il est à présent facile d'effectuer une boucle dans les cases à cocher pour toute opération concernant l'ensemble de ces cases. Par exemple, vous pouvez voir combien de cases sont activées en insérant le code suivant derrière un bouton du formulaire :

  

Dim obj As Object
Dim iCount As Integer = 0
For Each obj In colMyCheckBoxes
If TypeOf obj Is CheckBox Then
Dim chkCheckBox As CheckBox
chkCheckBox = CType(obj, CheckBox)
If chkCheckBox.Checked Then
iCount += 1
End If
End If
Next
MsgBox(iCount.ToString & " boxes checked")

 

Toutefois, il existe un raccourci qui vous évite de créer et de gérer la collection. Si vous placez les contrôles que vous souhaitez regrouper dans un panneau, vous obtiendrez automatiquement une collection les contenant tous, grâce à la propriété Controls du panneau.

Mettons cela en pratique. Supposons que nous ajoutions un panneau nommé Panel1 à notre formulaire et que nous y déplacions les trois cases à cocher. Nous pouvons à présent nous débarrasser du code permettant de créer la collection colMyCheckBoxes et le code derrière le bouton peut se présenter ainsi :

  

Dim ctl As Control
Dim iCount As Integer = 0
For Each ctl In Panel1.Controls
If TypeOf ctl Is CheckBox Then
Dim chkCheckBox As CheckBox
chkCheckBox = CType(ctl, CheckBox)
If chkCheckBox.Checked Then
iCount += 1
End If
End If
Next
MsgBox(iCount.ToString & " boxes checked")

 

L'un des avantages de cette technique est que la collection contient uniquement des contrôles. Cela signifie que certaines opérations n'ont pas besoin d'un Ctype pour convertir l'élément de collection en un type spécifique. Par exemple, si nous souhaitons mettre en gras la police de tous les contrôles de la collection, le code serait le suivant :

  

Dim ctl As Control
For Each ctl In Panel1.Controls
Dim fnt As New Font(ctl.Font, FontStyle.Bold)
ctl.Font = fnt
Next

 

Étant donné que tous les contrôles partagent une interface polymorphe incluant une propriété Font, la police de chaque contrôle de la collection sera alors mise en gras après l'exécution du code ci-dessus.

 

Combinaison des deux fonctionnalités

Les techniques de partage d'un événement entre plusieurs contrôles et d'insertion des contrôles dans une collection que nous venons de présenter fonctionnent parfaitement séparément. Rien ne les relie entre elles et du code doit être ajouté dans la première technique à chaque fois qu'un nouveau contrôle doit partager l'événement.

Toutefois, il est possible d'utiliser des fonctionnalités d'événement avancées dans Visual Basic .NET afin de créer une méthode permettant de combiner ces deux fonctionnalités. Nous pouvons créer une version spéciale d'un panneau qui raccorderait également automatiquement des événements de façon dynamique. De cette façon, nous nous rapprocherions de la fonctionnalité d'un tableau de contrôles, sans écrire aucun code dans nos formulaires pour gérer ce tableau de contrôles.

Le nouveau panneau est créé sous forme de nouveau type de contrôle à l'aide de l'héritage. J'ai précédemment expliqué la création des contrôles dans Visual Basic .NET je ne rentrerai donc pas ici dans les détails à ce propos. Je vous indiquerais juste la méthode à suivre étape par étape, en vous donnant des informations complémentaires sur la façon dont les événements sont gérés.

Nous souhaitons relier des événements de sorte que, lorsqu'un contrôle contenu dans le panneau déclenche un événement, celui-ci est collecté dans une routine commune, puis déclenché dans le formulaire qui héberge le panneau. Ceci est vraiment facile à réaliser. Voici comment procéder :

  1. Créez un nouveau projet Bibliothèque de contrôles Windows dans Visual Studio® .NET. Nommez-leControlPanelArray.

  2. Changez le nom du fichier UserControl1.vb dans l'Explorateur de solutions en ControlPanelArray.vb.

  3. Accédez au code de ControlPanelArray.vb. Les premières lignes devraient se présenter de la façon suivante :

    Public Class UserControl1
    Inherits System.Windows.Forms.UserControl
    Change them to look like this:
    Public Class ControlPanelArray
    Inherits System.Windows.Forms.Panel

    Ce code attribue donc un nom de classe à notre nouveau type de panneau (ControlPanelArray) et spécifie que la classe de base dont il hérite est le contrôle Windows Forms Panel.

  4. Nous pouvons faire en sorte que notre nouveau ControlPanelArray traite autant de types d'événements différents que nous le souhaitons, tant que l'événement fait partie de l'interface du type Control de base dont découlent tous les contrôles. Par exemple, ControlPanelArray gérera les événements suivants pour chaque contrôle du panneau :

    • Click
    • KeyPress
    • MouseUp

    Pour chacun d'entre eux, nous souhaitons intercepter les événements générés par les contrôles dans ControlArrayPanel et les consolider. L'événement consolidé peut ensuite être déclenché dans le formulaire contenant ControlArrayPanel. Cela signifie que ControlArrayPanel doit contenir des événements déclarés qui seront déclenchés dans le formulaire utilisant ControlArrayPanel. Les événements peuvent être déclarés de la façon suivante :

    Public Event InternalControlClick(ByVal sender As Object, _
    ByVal e As EventArgs)
    Public Event InternalControlKeyPress(ByVal sender As Object, _
    ByVal e As KeyPressEventArgs)
    Public Event InternalControlMouseUp(ByVal sender As Object, _
    ByVal e As MouseEventArgs)

    Insérez ce code juste au-dessous de la zone de code libellée Windows Forms Designer Generated Code.

  5. Afin d'intercepter chaque événement des contrôles dans ControlArrayPanel, il nous faut des gestionnaires d'événements que nous connectons de façon dynamique. Il nous en faut un pour chaque type d'événement à intercepter. Voici le code de trois gestionnaires d'événements utilisés pour les types d'événements mentionnés à l'étape 4 :

Private Sub InternalClickHandler(ByVal sender As Object, _
ByVal e As EventArgs)
RaiseEvent InternalControlClick(sender, e)
End Sub

Private Sub InternalKeyPressHandler(ByVal sender As Object, _
ByVal e As KeyPressEventArgs)
RaiseEvent InternalControlKeyPress(sender, e)
End Sub

Private Sub InternalMouseUpHandler(ByVal sender As Object, _
ByVal e As MouseEventArgs)
RaiseEvent internalControlMouseUp(sender, e)
End Sub

Ces événements, *internes* à **ControlArrayPanel**, déclenchent simplement l'événement consolidé correspondant, qui se déclenche *à l'extérieur* de **ControlArrayPanel**, dans le formulaire de consommation. Le code ci-dessus doit être inséré à la suite des déclarations d'événements de l'étape 4.
  1. Nous devons à présent raccorder ces gestionnaires d'événements lorsqu'un contrôle est ajouté au panneau. Ceci doit être effectué dans l'événement ControlAdded de ControlArrayPanel. Voici le code complet de l'événement ControlAdded qui peut apparaître directement sous le code ajouté à l'étape 5 :

    Private Sub ControlArrayPanel_ControlAdded(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.ControlEventArgs) _
    Handles MyBase.ControlAdded
    AddHandler e.Control.Click, AddressOf InternalClickHandler
    AddHandler e.Control.KeyPress, AddressOf InternalKeyPressHandler
    AddHandler e.Control.MouseUp, AddressOf InternalMouseUpHandler
    End Sub

    Si vous n'êtes pas un habitué du mot clé AddHandler, sachez qu'il relie de façon dynamique des événements déclenchés par des contrôles et des composants à des sous-routines données capables de gérer l'événement. C'est une façon de contourner la clause Handles que nous avons évoquée précédemment qui conduit au même résultat, à savoir raccorder un événement d'un contrôle donné à une routine pour gérer l'événement.

    La sous-routine qui sera reliée doit disposer de la liste d'arguments adéquate pour accepter l'événement. Par exemple, la routine InternalClickHandler requiert deux arguments : sender, du type Object, et e, du type EventArgs. Elle peut être reliée à un événement Click car la liste d'arguments de l'événement Click contient exactement les mêmes arguments. (Elle peut également être reliée aux événements DoubleClick et Enter car, comme nous l'avons vu précédemment, ces événements ont la même liste d'arguments.)

    Si vous essayez d'utiliser AddHandler pour relier e.Control.KeyPress à InternalClickHandler, vous obtiendrez une erreur de syntaxe indiquant « Method does not have the same signature as delegate » (La méthode n'a pas la même signature que le délégué) où les points de suspension sont remplacés par la description de la méthode et la liste d'arguments (parfois appelée signature de la méthode) de l'événement. (Faites un test pour afficher un message d'erreur détaillé.)

    Étant donné que cette logique a été placée dans l'événement ControlAdded, pour chaque contrôle ajouté à ControlArrayPanel, l'événement Click, l'événement KeyPress et l'événement MouseUp associés seront interceptés et transmis à la routine de consolidation des événements appropriée. Comme nous l'avons vu précédemment, ces routines déclencheront ensuite l'événement dans le formulaire de consommation.

  2. Afin de rendre notre ControlArrayPanel fiable, nous devons supprimer les gestionnaires lorsqu'un contrôle est supprimé de ControlArrayPanel. Créez alors un événement ControlRemoved se présentant comme le code ci-dessous et insérez le code juste sous l'événement ControlAdded ajouté à l'étape 6 :

    Private Sub ControlArrayPanel_ControlRemoved(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.ControlEventArgs) _
    Handles MyBase.ControlRemoved
    RemoveHandler e.Control.Click, AddressOf InternalClickHandler
    RemoveHandler e.Control.KeyPress, AddressOf InternalKeyPressHandler
    RemoveHandler e.Control.MouseUp, AddressOf InternalMouseUpHandler
    End Sub

    RemoveHandler interrompt la connexion entre l'événement du contrôle et la routine interne à ControlArrayPanel, gérant l'événement. La syntaxe de RemoveHandler est strictement identique à AddHandler.

  3. Nous disposons à présent d'un contrôle complet. Générons-le afin de nous assurer qu'il ne comporte pas d'erreur.

  4. Afin de tester le contrôle, sélectionnez Fichier | Ajouter un projet | Nouveau projet et, dans la boîte de dialogue Nouveau projet, sélectionnez un type de projet d'application Windows. Nommez le nouveau projet TestControlArrayPanel, puis appuyez sur OK.

  5. Le projet de test que nous venons d'ajouter doit devenir le projet lancé lorsque Déboguer | Démarrer est sélectionné (ou lorsque vous appuyez sur le bouton Démarrer de la barre d'outils). Cliquez ensuite avec le bouton droit de la souris sur la solution (soit la toute première entrée de l'Explorateur de solutions), puis sélectionnez Propriétés. Dans la boîte de dialogue Propriétés, sous le menu déroulant de Projet de démarrageunique, sélectionnez TestControlArrayPanel.

  6. Afin d'utiliser le contrôle ControlArrayPanel en mode glisser-déplacer, celui-ci doit être ajouté à la boîte à outils. Cliquez sur l'onglet Windows Forms de la boîte à outils avec le bouton droit de la souris et sélectionnez Personnaliser la boîte à outilsdans Visual Studio .NET 2002 ou Ajouter/supprimer des éléments dans Visual Studio .NET 2003. Recherchez ControlArrayPanel dans le projet, puis dans le répertoire bin. Il contient une DLL nommée ControlArrayPanel.dll. Sélectionnez-la, puis sélectionnez Ouvrir. Cliquez ensuite sur OK. Une icône de ControlArrayPanel apparaît en bas de la boîte à outils.

  7. Faites glisser un contrôle ControlArrayPanel dans le formulaire Form1 vide de TestControlArrayPanel. Il sera nommé ControlArrayPanel1. Faites ensuite glisser trois cases d'option dans ControlArrayPanel et définissez leur nom, ainsi que leurs propriétés de texte de la façon suivante :

    Nom de la propriété Définissez le texte sur :
    btnRed Red
    btnYellow Yellow
    btnLightGreen Light Green

     

  8. Faites glisser un bouton dansControlArrayPanel. Nommez-le btnRestore et définissez sa propriété de texte sur Restore Color.

  9. Allez dans la fenêtre de code de Form1. Dans le menu déroulant à gauche en haut de l'éditeur de code, sélectionnez ControlArrayPanel1. Sélectionnez ensuite l'événement InternalControlClick dans le menu déroulant à droite. Dans l'événement, ajoutez le code suivant :

    Dim ctlSender As Control
    ctlSender = CType(sender, Control)
    Select Case ctlSender.Name.ToUpper
    Case "optRed".ToUpper
    ControlArrayPanel1.BackColor = Color.Red
    Case "optYellow".ToUpper
    ControlArrayPanel1.BackColor = Color.Yellow
    Case "optLightGreen".ToUpper
    ControlArrayPanel1.BackColor = Color.LightGreen
    Case "btnRestore".ToUpper
    ControlArrayPanel1.BackColor = SystemColors.Control
    End Select

    Cet événement sera déclenché dès que vous cliquez sur l'un des contrôles de ControlArrayPanel. L'argument sender identifie le contrôle sur lequel vous avez cliqué dans ControlArrayPanel. Étant donné qu'il est du type Object, il doit être converti en un type Control pour pouvoir y accéder en tant que contrôle, et cette opération est exécutée par les deux premières lignes de ce code. Le contrôle peut alors être manipulé normalement.

  10. Revenez ensuite en mode Design du formulaire et double-cliquez sur le bouton de ControlArrayPanel pour créer un événement Click pour ce bouton. Dans l'événement du bouton, ajoutez le code suivant :

    MsgBox("Restore color button pressed")
    Dim ctl As Control
    For Each ctl In ControlArrayPanel1.Controls
    If TypeOf (ctl) Is RadioButton Then
    Dim ctlRadioButton As RadioButton
    ctlRadioButton = CType(ctl, RadioButton)
    ctlRadioButton.Checked = False
    End If
    Next

    Ce code démontre qu'il est possible d'effectuer une boucle dans les contrôles de ControlArrayPanel, ainsi qu'il était possible de le faire avec un panneau normal. Nous avons ainsi associé les deux techniques de consolidation des événements et d'accès à des contrôles multiples d'une collection en une seule classe, la classe ControlArrayPanel.

  11. Exécutez et testez l'application. Si vous appuyez sur la case d'option nommée Yellow, vous obtiendrez un résultat similaire à celui illustré à la Figure 1. Notez que lorsque vous appuyez sur le bouton, les deux routines d'événement se déclenchent dans le formulaire.

    Lorsqu'un événement est raccordé à plusieurs gestionnaires, vous ne devez pas être dépendant de l'ordre du déclenchement des gestionnaires d'événements. Vous devez faire en sorte que la logique de chaque gestionnaire soit entièrement indépendante de la logique des autres.

    Figure 1. ControlArrayPanel contenant trois cases d'option et un bouton. Vous avez appuyé sur la case d'option Yellow et l'événement Click de celle-ci a été routé vers un gestionnaire d'événements consolidé de Form1.

 

Ajout de contrôles à la volée à un formulaire

Pour ajouter un contrôle de façon dynamique à un formulaire Visual Basic 6.0, le contrôle devait faire partie d'un tableau de contrôles, ce qui représentait un frein important. En effet, cela impliquait qu'un contrôle « de départ » devait déjà exister sur le formulaire pour pouvoir créer le tableau de contrôles.

Toutes ces limitations concernant l'insertion de contrôles de façon dynamique sur un formulaire dans Visual Basic 6.0 ont maintenant disparu dans Windows Forms. En un sens, tous les contrôles sont insérés de façon dynamique sur le formulaire car le concepteur visuel crée le code permettant de créer les contrôles et de définir leurs propriétés. Vous pouvez utiliser la même syntaxe pour insérer des contrôles sur le formulaire à chaque fois que vous le souhaitez.

Pour ajouter une nouvelle zone de texte nommée txtExtraStuff sur un formulaire, vous pouvez utiliser une logique semblable à la suivante :

  

Dim txtExtraStuff As New TextBox
txtExtraStuff.Top = 40
txtExtraStuff.Left = 180
txtExtraStuff.Text = "Some stuff"
txtExtraStuff.Width = 130
Me.Controls.Add(txtExtraStuff)

 

Un nouveau contrôle est instancié comme toute autre classe et les propriétés appropriées peuvent ensuite être définies. Toute propriété non définie prendra sa valeur par défaut.

La dernière ligne rend le contrôle visible sur le formulaire. Le fait d'ajouter simplement le contrôle à la collection Controls du formulaire force le formulaire à intégrer le contrôle lors de la peinture de sa propre surface.

Une fois un contrôle ajouté, il peut être souhaitable de raccorder le contrôle à des gestionnaires d'événements. La syntaxe AddHandler que nous avons utilisée dans l'exemple précédent convient ici parfaitement. Incluez simplement une ligne AddHandler pour chaque événement du nouveau contrôle que vous souhaitez intercepter. Naturellement, vous devrez préalablement avoir codé les routines de votre gestionnaire d'événements.

Cette technique d'ajout de contrôles à la volée est si flexible et efficace qu'une fois que vous l'aurez utilisée, vous vous demanderez comment vous avez pu trouver tant d'atouts à l'ajout de contrôles dans un formulaire à l'aide des tableaux de contrôles.

Obtention d'une collection Forms

Les développeurs Visual Basic 6.0 aiment souvent passer d'un formulaire actuellement chargé à un autre dans une application pour effectuer des recherches, par exemple déterminer si un formulaire donné est chargé ou combien de formulaires d'un type donné sont chargés. Windows Forms n'inclut pas de collection Forms, et donc, l'obtention d'une telle fonctionnalité dans Visual Basic .NET nécessite des opérations supplémentaires.

Il est assez facile d'établir votre propre collection pour contenir les formulaires chargés, de la même façon que nous avons créé une collection de contrôles précédemment dans cet article. Le problème est d'accéder à la collection à partir de n'importe où dans une application et d'ajouter des formulaires de façon cohérente à travers l'application.

Une solution répondant parfaitement à ces besoins consiste à créer la collection sous forme de propriété partagée d'une classe de l'application. La classe peut également contenir une méthode partagée pour afficher un formulaire et cette méthode peut vérifier que le formulaire se trouve déjà dans la collection avant de l'afficher.

Pour créer une telle classe, lancez une nouvelle application Windows dans Visual Basic .NET. Sélectionnez Projet | Ajouter une classe. Nommez la nouvelle classe MyForms. Remplacez le code de la nouvelle classe par le code suivant :

  

Public Class MyForms

Private Shared m_colFormsCollection As New ArrayList
Public Shared Property Forms() As ArrayList
Get
Return m_colFormsCollection
End Get
Set(ByVal Value As ArrayList)
m_colFormsCollection = Value
End Set
End Property

Public Shared Sub Show(ByVal frm As Form)
Dim obj As Object
Dim bFound As Boolean = False
For Each obj In m_colFormsCollection
Dim frmCurrent As Form
frmCurrent = CType(obj, Form)
If frm Is frmCurrent Then
bFound = True
Exit For
End If
Next
If Not bFound Then
m_colFormsCollection.Add(frm)
End If
frm.Show()

End Sub
End Class

 

Ajoutez à présent un autre formulaire à votre application Windows. Nommez-le Form2. Allez dans l'aire de conception de Form1 et faites glisser deux boutons. Dans l'événement Click de Button1, ajoutez le code suivant :

  

Dim f As New Form2
MyForms.Show(f)

 

Dans l'événement Click de Button2, ajoutez le code suivant :

  

Dim obj As Form
For Each obj In MyForms.Forms
Dim frm As Form = CType(obj, Form)
frm.Visible = False
Next

 

Exécutez et testez le programme. Cliquez plusieurs fois sur le premier bouton pour obtenir des instances de Form2. Appuyez ensuite sur le second bouton et toutes les instances de Form2 deviendront invisibles.

De nombreux développeurs Visual Basic 6.0 auraient traité ce problème avec des variables globales en utilisant, par exemple, une variable globale pour une collection Forms. Toutefois, une classe avec des membres partagés, telle que celle utilisée ci-dessus, est préférable. Elle est accessible depuis n'importe où dans le projet, comme les variables globales, mais elle peut également disposer d'une logique permettant de gérer la collection de façon plus intelligente. La méthode Show ci-dessus vérifie qu'aucune tentative d'ajout d'un formulaire double à la collection n'est lancée, par exemple.

Différences avec la collection Controls

Dans Visual Basic 6.0, la collection Controls d'un formulaire renvoie tous les contrôles du formulaire, qu'ils soient ou non contenus dans un autre contrôle du formulaire, comme un cadre. Vous pouvez le constater en créant un formulaire Visual Basic 6.0 contenant un cadre avec deux boutons à l'intérieur et un bouton à l'extérieur du cadre, comme illustré à la Figure 2.

Figure 2. Formulaire Visual Basic 6.0 permettant de tester la collection Controls dans Visual Basic 6.0
Figure 2. Formulaire Visual Basic 6.0 permettant de tester la collection Controls dans Visual Basic 6.0

Insérez le code suivant derrière l'événement Click de Command3 dans Visual Basic 6.0 :

  

Dim ctl As Control
For Each ctl In Me.Controls
Debug.Print ctl.Caption
Next ctl

 

L'exécution du programme et le fait d'appuyer sur Command3 provoque l'apparition des quatre lignes suivantes dans la fenêtre de débogage :

  

Command3
Frame1
Command2
Command1

 

Si vous exécutez l'opération équivalente dans Visual Basic .NET, vous obtiendrez un résultat différent. Créez un formulaire similaire avec une zone de groupe et trois boutons dans Visual Basic .NET se présentant comme à la Figure 3.

Figure 3. Formulaire permettant de tester la collection Controls dans Visual Basic .NET

Figure 3. Formulaire permettant de tester la collection Controls dans Visual Basic .NET

Ajoutez la logique suivante dans l'événement Click de Button3 :

  

Dim ctl As Control
For Each ctl In Me.Controls
Console.WriteLine(ctl.Text)
Next

 

Si vous exécutez ce programme et que vous appuyez sur Button3, vous n'obtiendrez que deux lignes de sortie dans la fenêtre Sortie :

  

Button3
GroupBox1

 

Vous n'obtenez aucune sortie pour les deux boutons de la zone de groupe car ils ne se trouvent pas dans la collection Controls de Form1. Ils se trouvent au contraire dans la collection Controls de GroupBox1.

Dans Windows Forms, il est possible d'avoir des contrôles contenus dans des contrôles, eux-mêmes contenus dans d'autres contrôles et ce, jusqu'à un certain nombre de niveaux. Comment gérer alors tous ces contrôles sur un formulaire ?

C'est un cas classique où la récurrence est nécessaire. Nous pouvons créer une fonction qui effectue un traitement récursif dans un certain nombre de niveaux de conteneurs, jusqu'à ce qu'elle parvienne au dernier niveau et regroupe tous les contrôles trouvés, quel que soit le niveau.

Ajoutez les deux fonctions suivantes au formulaire Visual Basic .NET ci-dessus :

  

Private Function AllControls(ByVal frm As Form) As ArrayList
Dim colControls As New ArrayList
AddContainerControls(frm, colControls)
Return colControls
End Function

Private Sub AddContainerControls(ByVal ctlContainer As Control, _
ByVal colControls As ArrayList)
Dim ctl As Control
For Each ctl In ctlContainer.Controls
colControls.Add(ctl)
AddContainerControls(ctl, colControls)
Next
End Sub

 

La première fonction définit l'élément ArrayList qui sera utilisé pour contenir tous les contrôles et la seconde fonction y ajoute des contrôles, tandis qu'elle vérifie de façon récursive chaque contrôle pour voir si, à son tour, il contient d'autres contrôles.

Ajoutez à présent un nouveau bouton nommé Button4 au formulaire et insérez cette logique derrière ce bouton :

  

Dim ctl As Control
For Each ctl In AllControls(Me)
Console.WriteLine(ctl.Text)
Next

 

L'exécution du formulaire et le fait d'appuyer sur le bouton génère à présent une ligne de sortie pour chaque contrôle du formulaire, quel que soit son contenu. Par exemple, supposez que vous fassiez glisser d'autres contrôles sur le formulaire, incluant un panneau dans la zone de groupe et deux cases à cocher dans le panneau. Le résultat final ressemblerait à la Figure 4.

Figure 4. Formulaire permettant de tester la nouvelle collection AllControls dans Visual Basic .NET
Figure 4. Formulaire permettant de tester la nouvelle collection AllControls dans Visual Basic .NET

Exécutez à présent le programme et cliquez sur Button4. Le texte de sortie se présente de la façon suivante :

  

Button4
Button3
GroupBox1

CheckBox2
CheckBox1
Button2
Button1

 

Notez la ligne vide. Il s'agit de la ligne du panneau contenu dans la zone de groupe. Par défaut, la propriété de texte d'un contrôle de panneau est vide.

Conclusion

La transition entre Visual Basic 6.0 et Visual Basic .NET peut paraître quelque peu intimidante. En tant qu'environnement de développement entièrement orienté objet, Visual Basic .NET ne reprend pas certaines fonctionnalités de Visual Basic 6.0, comme les tableaux de contrôles destinés à l'origine à combler les manques de la fonctionnalité de l'objet. Apprendre à se passer de ces fonctionnalités peut être frustrant, mais comme nous l'avons vu, dans la plupart des cas elles existent toujours. Il vous suffit juste d'y accéder de façon différente. De plus, les nouvelles fonctionnalités de Visual Basic .NET offrent infiniment plus de flexibilité et de performance, rendant cette transition particulièrement bénéfique.


Billy Hollis est le co-auteur (avec Rocky Lhotka) de VB.NET Programming with the Public Beta (en anglais), le premier ouvrage jamais écrit sur Visual Basic .NET et il intervient régulièrement dans des conférences professionnelles majeures. Il a été directeur régional MSDN de Microsoft en 2001 et est membre du bureau des conférenciers de l'INETA de Microsoft. Il a également créé sa propre société de conseil en environnement .NET, spécialisée dans les logiciels commerciaux et le développement de clients intelligents. Il dispense également des formations Visual Basic .NET à travers les États-Unis.

Dernière mise à jour le lundi 4 août 2003