VB.NET Cosa è successo al controllo degli array

L'omissione delle matrici di controllo da VB.NET è una sfida per coloro che insegnano le matrici.

  • Non è più possibile semplicemente copiare un controllo, ad esempio una casella di testo, e quindi incollarlo (una o più volte) per creare un array di controllo.
  • Il codice VB.NET per la creazione di una struttura simile a un array di controllo è stato, in tutti i libri su VB.NET che ho acquistato e online, molto più lungo e molto più complesso. Manca la semplicità di codificare un array di controllo che si trova in VB6.

Se si fa riferimento alla libreria di compatibilità VB6, ci sono oggetti che agiscono in modo simile alle matrici di controllo. Per capire cosa intendo, è sufficiente utilizzare la procedura guidata di aggiornamento di VB.NET con un programma che contiene un array di controllo. Il codice è di nuovo brutto, ma funziona. La cattiva notizia è che Microsoft non garantirà che i componenti di compatibilità continueranno a essere supportati e non dovresti usarli.

Il codice VB.NET per creare e utilizzare "array di controllo" è molto più lungo e molto più complesso.

Secondo Microsoft, per fare qualcosa di simile a quello che si può fare in VB 6 è necessario creare un "semplice componente che duplica la funzionalità di array di controllo".

È necessario sia una nuova classe che un modulo di hosting per illustrare questo. La classe in realtà crea e distrugge nuove etichette. Il codice completo della classe è il seguente:

Classe pubblica LabelArray
    Eredita System.Collections.CollectionBase
    Private ReadOnly HostForm As _
    System.Windows.Forms.Form
    Funzione pubblica AddNewLabel () _
    Come System.Windows.Forms.Label
        'Crea una nuova istanza della classe Label.
        Dim aLabel As New System.Windows.Forms.Label
        'Aggiungi l'etichetta alla collezione
    'elenco interno.
        Me.List.Add (aLabel)
        'Aggiungi l'etichetta alla raccolta dei controlli   
        "del modulo a cui fa riferimento il campo HostForm.
        HostForm.Controls.Add (aLabel)
        'Imposta le proprietà iniziali per l'oggetto Label.
        aLabel.Top = Count * 25
        aLabel.Width = 50
        aLabel.Left = 140
        aLabel.Tag = Me.Count
        aLabel.Text = "Label" & Me.Count.ToString
        Restituisci etichetta
    Fine funzione
    Public Sub Nuovo (_
    Host ByVal come System.Windows.Forms.Form)
        HostForm = host
        Me.AddNewLabel ()
    End Sub
    Proprietà ReadOnly pubblica predefinita _
        Articolo (indice ByVal come intero) come _
        System.Windows.Forms.Label
        Ottenere
            Return CType (Me.List.Item (Index), _
        System.Windows.Forms.Label)
        End Get
    Proprietà finale
    Public Sub Remove ()
        'Verificare che sia presente un'etichetta da rimuovere.
        Se Me.Count> 0 Quindi
            'Rimuovi l'ultima etichetta aggiunta all'array 
            "dalla raccolta dei controlli del modulo host. 
        'Nota l'uso della proprietà predefinita in 
            'accesso all'array.
            HostForm.Controls.Remove (Me (Me.Count - 1))
            Me.List.RemoveAt (Me.Count - 1)
        Finisci se
    End Sub
End Class

Per illustrare come verrà utilizzato questo codice di classe, è possibile creare un modulo che lo chiama. Dovresti usare il codice mostrato sotto nel modulo:

Classe pubblica Form1 Eredita System.Windows.Forms.Form #Region "Codice generato da Progettazione Windows Form" 'Inoltre è necessario aggiungere l'istruzione:' MyControlArray = New LabelArray (Me) 'dopo la chiamata InitializeComponent () nel' codice regione nascosto. 'Dichiara un nuovo oggetto ButtonArray. Dim MyControlArray As LabelArray Private Sub btnLabelAdd_Click (_ ByVal mittente come System.Object, _ ByVal e As System.EventArgs) _ Handles btnLabelAdd.Click 'Chiama il metodo AddNewLabel' di MyControlArray. MyControlArray.AddNewLabel () 'Modifica la proprietà BackColor' del pulsante 0. MyControlArray (0) .BackColor = _ System.Drawing.Color.Red End Sottotitolo secondario privato btnLabelRemove_Click (_ ByVal mittente come System.Object, _ ByVal e As System .EventArgs) _ Gestisce btnLabelRemove.Click 'Chiama il metodo Remove di MyControlArray. MyControlArray.Remove () End Sub End Class

In primo luogo, questo non fa nemmeno il lavoro in Design Time come facevamo in VB 6! E in secondo luogo, non sono in un array, sono in una raccolta VB.NET - una cosa molto diversa da un array.

La ragione per cui VB.NET non supporta la "matrice di controllo" VB 6 è che non esiste una "matrice" di "controllo" (notare la modifica delle virgolette). VB 6 crea una raccolta dietro le quinte e la fa apparire come un array allo sviluppatore. Ma non è un array e hai poco controllo su di esso oltre le funzioni fornite tramite l'IDE.

VB.NET, d'altra parte, lo chiama così com'è: una raccolta di oggetti. E consegnano le chiavi del regno allo sviluppatore creando l'intera cosa all'aperto.

Come esempio del tipo di vantaggi che questo offre allo sviluppatore, in VB 6 i controlli dovevano essere dello stesso tipo e dovevano avere lo stesso nome. Poiché questi sono solo oggetti in VB.NET, puoi renderli di tipi diversi e assegnare loro nomi diversi e gestirli comunque nella stessa raccolta di oggetti.

In questo esempio, lo stesso evento Click gestisce due pulsanti e una casella di controllo e visualizza quello su cui è stato fatto clic. Fallo in una riga di codice con VB 6!

Sottotitoli privati ​​MixedControls_Click (_
    Mittente ByVal come System.Object, _
    ByVal e As System.EventArgs) _
    Pulsante Maniglie1. Fare clic su, _
            Button2.Click, _
            CheckBox1.Click
    'La seguente dichiarazione deve essere una lunga dichiarazione!
    'È su quattro righe qui per mantenerlo stretto
    'abbastanza per adattarsi a una pagina web
    Label2.Text = 
    Microsoft.VisualBasic.Right (sender.GetType.ToString, 
    Len (sender.GetType.ToString) - 
    (InStr (sender.GetType.ToString, "Forms") + 5))
End Sub

Il calcolo della sottostringa è piuttosto complesso, ma non è proprio quello di cui stiamo parlando qui. Puoi fare qualsiasi cosa nell'evento Click. Ad esempio, è possibile utilizzare il Tipo di controllo in un'istruzione If per eseguire operazioni diverse per controlli diversi.

Feedback del gruppo di studi di calcolo di Frank sugli array

Il gruppo di studio di Frank ha fornito un esempio con un modulo con 4 etichette e 2 pulsanti. Il pulsante 1 cancella le etichette e il pulsante 2 le riempie. È una buona idea leggere nuovamente la domanda originale di Frank e notare che l'esempio che ha usato era un ciclo che viene utilizzato per cancellare la proprietà Caption di una matrice di componenti Label. Ecco l'equivalente VB.NET di quel codice VB 6. Questo codice fa quello che Frank aveva inizialmente richiesto!

Classe pubblica Form1 Eredita System.Windows.Forms.Form #Region "Codice generato da Progettazione Windows Form" Dim LabelArray (4) Come etichetta 'dichiarare una matrice di etichette Private Sub Form1_Load (_ ByVal mittente come System.Object, _ ByVal e As System .EventArgs) _ Handles 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 mittente Come System.Object, _ ByVal e As System.EventArgs) _ Maniglie Button1.Click 'Pulsante 1 Cancella array Dim a As Intero per a = 1 a 4 LabelArray (a) .Text = "" Next End Sottotitoli secondari privati2_Click (_ Mittente ByVal come System.Object, _ ByVal e As System.EventArgs) _ Maniglie Button2.Click 'Button 2 Fill Array Dim a Asteger Per a = 1 a 4 LabelArray (a) .Text = _ "Control Array" & CStr ( a) Next End Sub End Class

Se si sperimenta questo codice, scoprirai che oltre a impostare le proprietà delle etichette, puoi anche chiamare metodi. Allora perché io (e Microsoft) abbiamo fatto tutto il possibile per creare il codice "Brutto" nella parte I dell'articolo?

Non sono d'accordo sul fatto che sia davvero un "Control Array" nel senso classico del VB. L'array di controllo VB 6 è una parte supportata della sintassi VB 6, non solo una tecnica. In effetti, forse il modo per descrivere questo esempio è che si tratta di una matrice di controlli, non di una matrice di controllo.

Nella parte I, mi sono lamentato del fatto che l'esempio di Microsoft funzionava SOLO in fase di esecuzione e non in fase di progettazione. È possibile aggiungere ed eliminare i controlli da un modulo in modo dinamico, ma tutto deve essere implementato nel codice. Non è possibile trascinare i controlli per crearli come è possibile in VB 6. Questo esempio funziona principalmente in fase di progettazione e non in fase di esecuzione. Non è possibile aggiungere ed eliminare i controlli in modo dinamico in fase di esecuzione. In un certo senso, è l'esatto contrario dell'esempio della Parte I..

Il classico esempio dell'array di controllo VB 6 è lo stesso implementato nel codice VB .NET. Qui nel codice VB 6 (questo è preso da Mezick e Hillier, Guida agli esami di certificazione Visual Basic 6, p 206 - leggermente modificato, poiché l'esempio nel libro risulta in controlli che non possono essere visti):

Dim MyTextBox come VB.TextBox Statico intNumber come Numero intero intNumber = intNumber + 1 Imposta MyTextBox = _ Me.Controls.Add ("VB.TextBox", _ "Text" & intNumber) MyTextBox.Text = MyTextBox.Name MyTextBox.Visible = True MyTextBox.Left = _ (intNumber - 1) * 1200

Ma, come concordano Microsoft (e I), in VB.NET non sono possibili array di controllo VB 6. Quindi il meglio che puoi fare è duplicare la funzionalità. Il mio articolo ha duplicato la funzionalità trovata nell'esempio di Mezick & Hillier. Il codice del gruppo di studio duplica la funzionalità di poter impostare proprietà e metodi di chiamata.

Quindi la linea di fondo è che dipende davvero da cosa vuoi fare. VB.NET non ha tutto racchiuso come parte del linguaggio - Eppure - ma alla fine è molto più flessibile.

Take on Control Arrays di John Fannon

John ha scritto: Avevo bisogno di array di controllo perché volevo mettere una semplice tabella di numeri in un modulo in fase di esecuzione. Non volevo la nausea nel metterli tutti singolarmente e volevo usare VB.NET. Microsoft offre una soluzione molto dettagliata a un semplice problema, ma è una mazza molto grande per rompere un dado molto piccolo. Dopo un po 'di sperimentazione, alla fine ho trovato una soluzione. Ecco come l'ho fatto.

L'esempio Informazioni su Visual Basic sopra mostra come è possibile creare una casella di testo in un modulo creando un'istanza dell'oggetto, impostando le proprietà e aggiungendola alla raccolta Controls che fa parte dell'oggetto Form.

Dim txtDataShow As New TextBox
txtDataShow.Height = 19
txtDataShow.Width = 80
txtDataShow.Location = Nuovo punto (X, Y)
Me.Controls.Add (txtDataShow)
Sebbene la soluzione Microsoft crei una classe, ho pensato che sarebbe stato possibile racchiudere tutto ciò in una subroutine. Ogni volta che si chiama questa subroutine, si crea una nuova istanza della casella di testo nel modulo. Ecco il codice completo:

Modulo di classe pubblica 1
    Eredita System.Windows.Forms.Form

#Region "Codice generato da Progettazione Windows Form"

    Private Sub BtnStart_Click (_
        Mittente ByVal come System.Object, _
        ByVal e As System.EventArgs) _
        Maniglie btnStart.Click

        Dim I come intero
        Dim sData As String
        Per I = 1 a 5
            sData = CStr (I)
            Chiama AddDataShow (sData, I)
        Il prossimo
    End Sub
    AddDataShow secondario (_
        ByVal sText As String, _
        ByVal I come numero intero)

        Dim txtDataShow As New TextBox
        Dim UserLft, UserTop As Intero
        Dim X, Y come numero intero
        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 = Nuovo punto (X, Y)
        Me.Controls.Add (txtDataShow)
    End Sub
End Class
Ottimo punto, John. Questo è sicuramente molto più semplice del codice Microsoft ... quindi mi chiedo perché abbiano insistito a farlo in quel modo?

Per iniziare la nostra indagine, proviamo a modificare una delle assegnazioni di proprietà nel codice. Facciamo cambio

txtDataShow.Height = 19
per

txtDataShow.Height = 100
solo per assicurarsi che ci sia una notevole differenza.

Quando eseguiamo di nuovo il codice, otteniamo ... Whaaaat ??? ... la stessa cosa. Nessun cambiamento. In effetti, è possibile visualizzare il valore con un'istruzione come MsgBox (txtDataShow.Height) e ottenere comunque 20 come valore della proprietà, indipendentemente da ciò che si assegna ad esso. Perché succede??

La risposta è che non stiamo derivando la nostra classe per creare gli oggetti, stiamo solo aggiungendo cose a un'altra classe, quindi dobbiamo seguire le regole dell'altra classe. E quelle regole affermano che non è possibile modificare la proprietà Height. (Bene ... puoi farlo. Se cambi la proprietà Multiline in True, puoi cambiare l'altezza.)

Perché VB.NET va avanti ed esegue il codice senza nemmeno un lamento che potrebbe esserci qualcosa di sbagliato quando, in realtà, ignora totalmente la tua affermazione è un tutt'altra lamentela. Potrei suggerire almeno un avvertimento nella compilazione, tuttavia. (Suggerimento! Suggerimento! Suggerimento! Microsoft è in ascolto?)

L'esempio della Parte I eredita da un'altra Classe e questo rende le proprietà disponibili per il codice nella Classe ereditaria. La modifica della proprietà Height su 100 in questo esempio ci dà i risultati previsti. (Ancora una volta ... una dichiarazione di non responsabilità: quando viene creata una nuova istanza di un componente Label di grandi dimensioni, copre quella precedente. Per visualizzare effettivamente i nuovi componenti Label, è necessario aggiungere la chiamata al metodo aLabel.BringToFront ().)

Questo semplice esempio mostra che, sebbene possiamo semplicemente aggiungere oggetti a un'altra classe (e talvolta questa è la cosa giusta da fare), il controllo della programmazione sugli oggetti richiede che li deriviamo in una classe e nel modo più organizzato (oserei dire, "il modo .NET" ??) è creare proprietà e metodi nella nuova Classe derivata per cambiare le cose. All'inizio John non rimase convinto. Ha detto che il suo nuovo approccio si adatta al suo scopo anche se ci sono dei limiti a non essere "COO" (Orientato correttamente agli oggetti). Più recentemente, tuttavia, John ha scritto,

"... dopo aver scritto un set di 5 caselle di testo in fase di esecuzione, volevo aggiornare i dati in una parte successiva del programma - ma nulla è cambiato - i dati originali erano ancora lì.

Ho scoperto che avrei potuto aggirare il problema scrivendo il codice per rimuovere le vecchie scatole e rimetterle di nuovo con nuovi dati. Un modo migliore per farlo sarebbe usare Me.Refresh. Ma questo problema ha attirato la mia attenzione sulla necessità di fornire un metodo per sottrarre le caselle di testo e aggiungerle ".

Il codice di John utilizzava una variabile globale per tenere traccia di quanti controlli erano stati aggiunti al modulo, quindi un metodo ...

Sottomodulo privato1_Load (_
   Mittente ByVal come System.Object, _
   ByVal e As System.EventArgs) _
   Gestisce MyBase.Load
   CntlCnt0 = Me.Controls.Count
End Sub

Quindi il controllo "ultimo" potrebbe essere rimosso ...

N = Me.Controls.Count - 1
Me.Controls.RemoveAt (N)
John notò che "forse è un po 'goffo".

È il modo in cui Microsoft tiene traccia degli oggetti in COM E nel loro codice di esempio "brutto" sopra.

Ora sono tornato al problema della creazione dinamica di controlli in un modulo in fase di esecuzione e ho rivisto gli articoli "Che cosa è successo al controllo degli array".

Ho creato le classi e ora posso posizionare i controlli sul modulo nel modo in cui voglio che siano.

John ha dimostrato come controllare il posizionamento dei controlli in una casella di gruppo utilizzando le nuove classi che ha iniziato a utilizzare. Forse Microsoft aveva ragione nella sua soluzione "brutta" dopo tutto!