VB.NET Que s'est-il passé pour contrôler les tableaux

L'omission des tableaux de contrôle de VB.NET est un défi pour ceux qui enseignent les tableaux.

  • Il n'est plus possible de copier simplement un contrôle, tel qu'une zone de texte, puis de le coller (une ou plusieurs fois) pour créer un tableau de contrôles.
  • Le code VB.NET pour créer une structure similaire à un tableau de contrôle a été, dans tous les livres sur VB.NET que j'ai achetés et en ligne, beaucoup plus long et beaucoup plus complexe. Il manque la simplicité de codage d'un tableau de contrôle qui se trouve dans VB6.

Si vous référencez la bibliothèque de compatibilité VB6, il y a des objets qui agissent à peu près comme des tableaux de contrôle. Pour voir ce que je veux dire, utilisez simplement l'assistant de mise à niveau VB.NET avec un programme qui contient un tableau de contrôle. Le code est à nouveau laid, mais cela fonctionne. La mauvaise nouvelle est que Microsoft ne garantit pas que les composants de compatibilité continueront d'être pris en charge et que vous n'êtes pas censé les utiliser.

Le code VB.NET pour créer et utiliser des "tableaux de contrôle" est beaucoup plus long et beaucoup plus complexe.

Selon Microsoft, pour faire quelque chose de même proche de ce que vous pouvez faire dans VB 6, il faut créer un "composant simple qui duplique la fonctionnalité du tableau de contrôle".

Vous avez besoin à la fois d'une nouvelle classe et d'un formulaire d'hébergement pour illustrer cela. La classe crée et détruit en fait de nouvelles étiquettes. Le code de classe complet est le suivant:

LabelArray de classe publique
    Hérite de System.Collections.CollectionBase
    Private ReadOnly HostForm As _
    System.Windows.Forms.Form
    Fonction publique AddNewLabel () _
    En tant que System.Windows.Forms.Label
        'Créer une nouvelle instance de la classe Label.
        Dim aLabel As New System.Windows.Forms.Label
        'Ajoutez l'étiquette à la collection
    «liste interne.
        Me.List.Add (aLabel)
        'Ajouter l'étiquette à la collection Controls   
        'du formulaire référencé par le champ HostForm.
        HostForm.Controls.Add (aLabel)
        'Définir les propriétés initiales de l'objet Label.
        aLabel.Top = Count * 25
        aLabel.Width = 50
        aLabel.Left = 140
        aLabel.Tag = Me.Count
        aLabel.Text = "Label" & Me.Count.ToString
        Renvoyer une étiquette
    Fonction de fin
    Public Sub New (_
    Hôte ByVal en tant que System.Windows.Forms.Form)
        HostForm = hôte
        Me.AddNewLabel ()
    End Sub
    Propriété publique en lecture seule par défaut _
        Élément (indice ByVal en tant qu'entier) en tant que _
        System.Windows.Forms.Label
        Avoir
            Renvoyer CType (Me.List.Item (Index), _
        System.Windows.Forms.Label)
        Fin Get
    Propriété de fin
    Sous-public Supprimer ()
        'Vérifiez qu'il y a une étiquette à retirer.
        Si Me.Count> 0 Then
            'Supprimer la dernière étiquette ajoutée au tableau 
            'de la collection de contrôles de formulaire hôte. 
        'Notez l'utilisation de la propriété par défaut dans 
            'accéder au tableau.
            HostForm.Controls.Remove (Me (Me.Count - 1))
            Me.List.RemoveAt (Me.Count - 1)
        Fin si
    End Sub
Fin de classe

Pour illustrer comment ce code de classe serait utilisé, vous pouvez créer un formulaire qui l'appelle. Vous devrez utiliser le code ci-dessous dans le formulaire:

La classe publique Form1 hérite de System.Windows.Forms.Form #Region "Code généré par le Concepteur de formulaires Windows" "Vous devez également ajouter l'instruction:" MyControlArray = New LabelArray (Me) "après l'appel InitializeComponent () dans le" code de région masqué ". 'Déclarez un nouvel objet ButtonArray. Dim MyControlArray As LabelArray Private Sub btnLabelAdd_Click (_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnLabelAdd.Click 'Call the AddNewLabel method' of MyControlArray. MyControlArray.AddNewLabel () 'Modifier la propriété BackColor' du Button 0. MyControlArray (0) .BackColor = _ System.Drawing.Color.Red End Sub Private Sub btnLabelRemove_Click (_ ByVal sender As System.Object, _ ByVal e As System .EventArgs) _ Gère btnLabelRemove.Click 'Appelez la méthode Remove de MyControlArray. MyControlArray.Remove () End Sub End Class

Tout d'abord, cela ne fait même pas le travail au Design Time comme nous le faisions dans VB 6! Et deuxièmement, ils ne sont pas dans un tableau, ils sont dans une collection VB.NET - une chose très différente d'un tableau.

La raison pour laquelle VB.NET ne prend pas en charge le "tableau de contrôle" VB 6 est qu'il n'existe pas de "tableau" de "contrôle" (notez le changement de guillemets). VB 6 crée une collection en arrière-plan et la fait apparaître comme un tableau pour le développeur. Mais ce n'est pas un tableau et vous avez peu de contrôle sur lui au-delà des fonctions fournies par l'IDE.

VB.NET, d'autre part, l'appelle ce qu'il est: une collection d'objets. Et ils remettent les clés du royaume au développeur en créant le tout à découvert.

Comme exemple du type d'avantages que cela donne au développeur, dans VB 6, les contrôles devaient être du même type, et ils devaient avoir le même nom. Comme ce ne sont que des objets dans VB.NET, vous pouvez leur donner différents types et leur donner des noms différents tout en les gérant dans la même collection d'objets.

Dans cet exemple, le même événement Click gère deux boutons et une case à cocher et affiche celui sur lequel vous avez cliqué. Faites cela en une seule ligne de code avec VB 6!

Private Sub MixedControls_Click (_
    Expéditeur ByVal As System.Object, _
    ByVal e As System.EventArgs) _
    Poignées Button1.Click, _
            Button2.Click, _
            CheckBox1.Click
    «La déclaration ci-dessous doit être une longue déclaration!
    «Il est sur quatre lignes ici pour le garder étroit
    'assez pour tenir sur une page web
    Label2.Text = 
    Microsoft.VisualBasic.Right (sender.GetType.ToString, 
    Len (sender.GetType.ToString) - 
    (InStr (sender.GetType.ToString, "Forms") + 5))
End Sub

Le calcul de la sous-chaîne est assez complexe, mais ce n'est pas vraiment ce dont nous parlons ici. Vous pouvez faire n'importe quoi dans l'événement Click. Vous pouvez, par exemple, utiliser le Type du contrôle dans une instruction If pour faire différentes choses pour différents contrôles.

Commentaires du Frank's Computing Studies Group sur les tableaux

Le groupe d'étude de Frank a fourni un exemple avec un formulaire qui a 4 étiquettes et 2 boutons. Le bouton 1 efface les étiquettes et le bouton 2 les remplit. C'est une bonne idée de relire la question originale de Frank et de remarquer que l'exemple qu'il a utilisé était une boucle qui est utilisée pour effacer la propriété Caption d'un tableau de composants Label. Voici l'équivalent VB.NET de ce code VB 6. Ce code fait ce que Frank avait initialement demandé!

La classe publique Form1 hérite de System.Windows.Forms.Form #Region "Code généré par Windows Form Designer" Dim LabelArray (4) As Label 'déclare un tableau d'étiquettes Private Sub Form1_Load (_ ByVal sender As System.Object, _ ByVal e As System .EventArgs) _ Gère MyBase.Load SetControlArray () End Sub Sub SetControlArray () LabelArray (1) = Label1 LabelArray (2) = Label2 LabelArray (3) = Label3 LabelArray (4) = Label4 End Sub Private Sub Button1_Click (_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Poignées Button1.Click 'Button 1 Clear Array Dim a As Integer Pour a = 1 à 4 LabelArray (a) .Text = "" Next End Sub Private Sub Button2_Click (_ Expéditeur ByVal As System.Object, _ ByVal e As System.EventArgs) _ Poignées Button2.Click 'Button 2 Fill Array Dim a As Integer Pour a = 1 à 4 LabelArray (a) .Text = _ "Control Array" & CStr ( a) Classe End End Sub End suivante

Si vous expérimentez ce code, vous découvrirez qu'en plus de définir les propriétés des étiquettes, vous pouvez également appeler des méthodes. Alors pourquoi ai-je (et Microsoft) fait tout ce qui était en mon pouvoir pour créer le code "laid" dans la partie I de l'article?

Je ne suis pas d'accord que c'est vraiment un "Control Array" dans le sens VB classique. Le tableau de contrôle VB 6 est une partie prise en charge de la syntaxe VB 6, pas seulement une technique. En fait, peut-être que la façon de décrire cet exemple est qu'il s'agit d'un tableau de contrôles, pas d'un tableau de contrôles.

Dans la partie I, je me plaignais que l'exemple de Microsoft ne fonctionnait QUE lors de l'exécution et non lors de la conception. Vous pouvez ajouter et supprimer des contrôles d'un formulaire dynamiquement, mais le tout doit être implémenté dans le code. Vous ne pouvez pas faire glisser et déposer des contrôles pour les créer comme vous le pouvez dans VB 6. Cet exemple fonctionne principalement au moment de la conception et non au moment de l'exécution. Vous ne pouvez pas ajouter et supprimer des contrôles dynamiquement au moment de l'exécution. D'une certaine manière, c'est l'opposé complet de l'exemple de la partie I.

L'exemple classique de tableau de contrôle VB 6 est le même qui est implémenté dans le code VB .NET. Ici dans le code VB 6 (ceci est tiré de Mezick & Hillier, Guide d'examen de certification Visual Basic 6, p 206 - légèrement modifié, car l'exemple du livre entraîne des contrôles invisibles):

Dim MyTextBox as VB.TextBox Static intNumber as Integer intNumber = intNumber + 1 Set MyTextBox = _ Me.Controls.Add ("VB.TextBox", _ "Text" & intNumber) MyTextBox.Text = MyTextBox.Name MyTextBox.Visible = True MyTextBox.Left = _ (intNumber - 1) * 1200

Mais comme Microsoft (et moi) sommes d'accord, les tableaux de contrôle VB 6 ne sont pas possibles dans VB.NET. Donc, le mieux que vous puissiez faire est de dupliquer la fonctionnalité. Mon article a dupliqué la fonctionnalité trouvée dans l'exemple Mezick & Hillier. Le code du groupe d'étude duplique la fonctionnalité de pouvoir définir des propriétés et des méthodes d'appel.

Donc, l'essentiel est que cela dépend vraiment de ce que vous voulez faire. VB.NET n'a pas le tout enveloppé dans le langage - Pourtant - mais en fin de compte, il est beaucoup plus flexible.

Prise en charge des baies de contrôle par John Fannon

John a écrit: J'avais besoin de tableaux de contrôle parce que je voulais mettre un simple tableau de nombres sur un formulaire au moment de l'exécution. Je ne voulais pas avoir la nausée de les placer tous individuellement et je voulais utiliser VB.NET. Microsoft offre une solution très détaillée à un problème simple, mais c'est un très gros marteau pour casser un très petit écrou. Après quelques expérimentations, j'ai finalement trouvé une solution. Voici comment je l'ai fait.

L'exemple À propos de Visual Basic ci-dessus montre comment vous pouvez créer un TextBox sur un formulaire en créant une instance de l'objet, en définissant des propriétés et en l'ajoutant à la collection Controls qui fait partie de l'objet Form.

Dim txtDataShow comme nouveau TextBox
txtDataShow.Height = 19
txtDataShow.Width = 80
txtDataShow.Location = Nouveau point (X, Y)
Me.Controls.Add (txtDataShow)
Bien que la solution Microsoft crée une classe, j'ai pensé qu'il serait possible d'envelopper tout cela dans un sous-programme à la place. Chaque fois que vous appelez ce sous-programme, vous créez une nouvelle instance de la zone de texte sur le formulaire. Voici le code complet:

Formule de classe publique1
    Hérite de System.Windows.Forms.Form

#Region "Code généré par Windows Form Designer"

    Private Sub BtnStart_Click (_
        Expéditeur ByVal As System.Object, _
        ByVal e As System.EventArgs) _
        Poignées btnStart.Click

        Dim I as Integer
        Dim sData As String
        Pour I = 1 à 5
            sData = CStr (I)
            Appelez AddDataShow (sData, I)
        Prochain
    End Sub
    Sub AddDataShow (_
        ByVal sText As String, _
        ByVal I en tant que nombre entier)

        Dim txtDataShow comme nouveau TextBox
        Dim UserLft, UserTop As Integer
        Dim X, Y comme entier
        UserLft = 20
        UserTop = 20
        txtDataShow.Height = 19
        txtDataShow.Width = 25
        txtDataShow.TextAlign = _
            HorizontalAlignment.Center
        txtDataShow.BorderStyle = _
            BorderStyle.FixedSingle
        txtDataShow.Text = sText
        X = UserLft
        Y = UserTop + (I - 1) * txtDataShow.Height
        txtDataShow.Location = Nouveau point (X, Y)
        Me.Controls.Add (txtDataShow)
    End Sub
Fin de classe
Très bon point, John. C'est certainement beaucoup plus simple que le code Microsoft… alors je me demande pourquoi ils ont insisté pour le faire de cette façon?

Pour commencer notre enquête, essayons de changer l'une des affectations de propriété dans le code. Changeons

txtDataShow.Height = 19
à

txtDataShow.Height = 100
juste pour s'assurer qu'il y a une différence notable.

Lorsque nous exécutons à nouveau le code, nous obtenons… Whaaaat ???… la même chose. Aucun changement du tout. En fait, vous pouvez afficher la valeur avec une instruction comme MsgBox (txtDataShow.Height) et vous obtenez toujours 20 comme valeur de la propriété, peu importe ce que vous lui affectez. Pourquoi est-ce que cela se produit?

La réponse est que nous ne dérivons pas notre propre classe pour créer les objets, nous ajoutons simplement des choses à une autre classe, nous devons donc suivre les règles de l'autre classe. Et ces règles stipulent que vous ne pouvez pas modifier la propriété Height. (Eh bien, vous pouvez. Si vous modifiez la propriété Multiline sur True, vous pouvez modifier la hauteur.)

Pourquoi VB.NET va de l'avant et exécute le code sans même gémir qu'il pourrait y avoir quelque chose de mal alors qu'en fait, il ignore totalement votre déclaration est un tout autre reproche. Je pourrais suggérer au moins un avertissement dans la compilation, cependant. (Indice! Indice! Indice! Microsoft écoute-t-il?)

L'exemple de la partie I hérite d'une autre classe, ce qui rend les propriétés disponibles pour le code de la classe héritée. La modification de la propriété Height à 100 dans cet exemple nous donne les résultats attendus. (Encore une fois, une clause de non-responsabilité: lorsqu'une nouvelle instance d'un grand composant Label est créée, elle recouvre l'ancienne. Pour voir réellement les nouveaux composants Label, vous devez ajouter la méthode appelée aLabel.BringToFront ().)

Cet exemple simple montre que, bien que nous puissions simplement ajouter des objets à une autre classe (et parfois c'est la bonne chose à faire), la programmation du contrôle sur les objets nécessite que nous les dérivions d'une classe et de la manière la plus organisée (oserais-je dire, "la manière .NET" ??) consiste à créer des propriétés et des méthodes dans la nouvelle classe dérivée pour changer les choses. John n'était pas convaincu au début. Il a dit que sa nouvelle approche convenait à son objectif même s'il y avait des limites à ne pas être "COO" (Correctly Object Oriented). Plus récemment, cependant, John a écrit,

"... après avoir écrit un ensemble de 5 zones de texte lors de l'exécution, je voulais mettre à jour les données dans une partie ultérieure du programme - mais rien n'a changé - les données d'origine étaient toujours là.

J'ai trouvé que je pouvais contourner le problème en écrivant du code pour enlever les anciennes boîtes et les remettre à nouveau avec de nouvelles données. Une meilleure façon de le faire serait d'utiliser Me.Refresh. Mais ce problème a attiré mon attention sur la nécessité de fournir une méthode pour soustraire les zones de texte et les ajouter. "

Le code de John utilisait une variable globale pour garder une trace du nombre de contrôles ajoutés au formulaire, donc une méthode…

Sous-formulaire privé1_Load (_
   Expéditeur ByVal As System.Object, _
   ByVal e As System.EventArgs) _
   Gère MyBase.Load
   CntlCnt0 = Me.Controls.Count
End Sub

Ensuite, le "dernier" contrôle pourrait être supprimé ...

N = Me.Controls.Count - 1
Me.Controls.RemoveAt (N)
John a noté que "c'est peut-être un peu maladroit".

C'est la façon dont Microsoft garde la trace des objets dans COM ET dans leur exemple de code "laid" ci-dessus.

Je suis maintenant revenu au problème de la création dynamique de contrôles sur un formulaire au moment de l'exécution et j'ai revu les articles "What Happened to Control Arrays".

J'ai créé les classes et je peux maintenant placer les contrôles sur le formulaire comme je veux qu'ils soient.

John a montré comment contrôler le placement des contrôles dans une zone de groupe en utilisant les nouvelles classes qu'il a commencé à utiliser. Peut-être que Microsoft avait raison dans leur solution "laide" après tout!