L'omissione delle matrici di controllo da VB.NET è una sfida per coloro che insegnano le matrici.
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.
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.
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!