Utelämnandet av kontrollmatriser från VB.NET är en utmaning för dem som undervisar om matriser.
Om du hänvisar till VB6-kompatibilitetsbiblioteket finns det objekt där som fungerar ganska mycket som kontrollmatriser. För att se vad jag menar, använd bara VB.NET-uppgraderingsguiden med ett program som innehåller en kontrollgrupp. Koden är ful igen, men den fungerar. De dåliga nyheterna är att Microsoft inte kommer att garantera att kompatibilitetskomponenterna kommer att fortsätta att stödjas, och du ska inte använda dem.
VB.NET-koden för att skapa och använda "kontrollmatriser" är mycket längre och mycket mer komplex.
Enligt Microsoft kräver skapandet en "enkel komponent som duplicerar kontrollfunktionens funktionalitet" för att göra något till och med vad du kan göra i VB 6.
Du behöver både en ny klass och ett värdformulär för att illustrera detta. Klassen skapar och förstör faktiskt nya etiketter. Den fullständiga klasskoden är som följer:
Public Class LabelArray
Inherits System.Collections.CollectionBase
Privat ReadOnly HostForm As _
System.Windows.Forms.Form
Public Function AddNewLabel () _
Som System.Windows.Forms.Label
"Skapa en ny instans av Label-klassen.
Dim aLabel som nytt system.Windows.Forms.Label
Lägg till etiketten i samlingen
"intern lista.
Me.List.Add (Alabel)
Lägg till etiketten i kontroller-samlingen
'i formuläret som refereras av fältet HostForm.
HostForm.Controls.Add (Alabel)
'Ställ in intima egenskaper för Label-objektet.
aLabel.Top = Räkna * 25
aLabel. Bredd = 50
aLabel.Left = 140
aLabel.Tag = Me.Count
aLabel.Text = "Label" & Me.Count.ToString
Retur aLabel
Avsluta funktion
Public Sub New (_
ByVal host As System.Windows.Forms.Form)
HostForm = värd
Me.AddNewLabel ()
Avsluta under
Standard Public ReadOnly Property _
Objekt (ByVal Index som heltal) Som _
System.Windows.Forms.Label
Skaffa sig
Returnera CType (Me.List.Item (Index), _
System.Windows.Forms.Label)
Slut Get
Slut egendom
Public Sub Ta bort ()
"Kontrollera att det finns en etikett att ta bort.
If Me.Count> 0 Then
'Ta bort den sista etiketten som lagts till i matrisen
"från värdformuläret kontrollerar samlingen.
'Notera användningen av standardegenskapen i
"åtkomst till matrisen.
HostForm.Controls.Remove (Me (Me.Count - 1))
Me.List.RemoveAt (Me.Count - 1)
Sluta om
Avsluta under
Slutklass
För att illustrera hur denna klasskod skulle användas kan du skapa ett formulär som kallar det. Du måste använda koden som visas nedan i formuläret:
Public Class Form1 Inherits System.Windows.Forms.Form #Region "Windows Form Designer genererad kod" 'Du måste också lägga till uttalandet:' MyControlArray = New LabelArray (Me) 'efter InitializeComponent () anropa den' dolda regionkoden. "Förklara ett nytt ButtonArray-objekt. Dim MyControlArray As LabelArray Private Sub btnLabelAdd_Click (_ ByVal avsändare som system.Objekt, _ ByVal e As System.EventArgs) _ Hanterar btnLabelAdd.Click 'Call the AddNewLabel method' för MyControlArray. MyControlArray.AddNewLabel () 'Ändra egenskapen BackColor' i knappen 0. MyControlArray (0) .BackColor = _ System.Drawing.Color.Red End Sub Private Sub btnLabelRemove_Click (_ ByVal avsändare som System.Object, _ ByVal e As System .EventArgs) _ Hanterar btnLabelRemove.Click 'Ring bort metoden för MyControlArray. MyControlArray.Remove () End Sub End Class
Först gör detta inte ens jobbet på Design Time som vi brukade göra det i VB 6! Och för det andra är de inte i en matris, de finns i en VB.NET-samling - en mycket annorlunda sak än en matris.
Anledningen till att VB.NET inte stöder VB 6 "kontrollarray" är att det inte finns något sådant som ett "kontroll" "array" (notera ändringen av citattecken). VB 6 skapar en samling bakom kulisserna och får den att visas som en matris för utvecklaren. Men det är inte ett array och du har liten kontroll över det utöver de funktioner som tillhandahålls genom IDE.
VB.NET, å andra sidan, kallar det vad det är: en samling av objekt. Och de överlämnar nycklarna till kungariket till utvecklaren genom att skapa hela saken helt utomhus.
Som ett exempel på vilken typ av fördelar detta ger utvecklaren, i VB 6 måste kontrollerna vara av samma typ, och de måste ha samma namn. Eftersom detta bara är objekt i VB.NET, kan du göra dem olika typer och ge dem olika namn och ändå hantera dem i samma samling av objekt.
I det här exemplet hanterar samma Click-händelse två knappar och en kryssruta och visar vilken som klickades på. Gör det i en kodrad med VB 6!
Privat sub MixedControls_Click (_
ByVal avsändare som system.Objekt, _
ByVal e As System.EventArgs) _
Handtag-knapp1.Klicka, _
Knapp2.Klicka, _
CheckBox1.Click
”Uttalandet nedan måste vara ett långt uttalande!
"Det är på fyra rader här för att hålla det smalt
tillräckligt för att passa på en webbsida
Label2.Text =
Microsoft.VisualBasic.Right (sender.GetType.ToString,
Len (sender.GetType.ToString) -
(InStr (avsändare.GetType.ToString, "Forms") + 5))
Avsluta under
Substringsberäkningen är typ av komplex, men det är inte riktigt vad vi pratar om här. Du kan göra vad som helst i Click-evenemanget. Du kan till exempel använda typ av kontroll i ett If-uttalande för att göra olika saker för olika kontroller.
Frank's Study Group gav ett exempel med ett formulär som har fyra etiketter och 2 knappar. Knapp 1 rensar etiketterna och knapp 2 fyller dem. Det är en bra idé att läsa Franks ursprungliga fråga igen och märka att exemplet han använde var en slinga som används för att rensa bildtextsegenskapen för en rad etikettkomponenter. Här är VB.NET-motsvarigheten till den VB 6-koden. Den här koden gör vad Frank ursprungligen bad om!
Public Class Form1 Inherits System.Windows.Forms.Form #Region "Windows Form Designer genererad kod" Dim LabelArray (4) Som Label förklarar en rad etiketter Privat Sub Form1_Load (_ ByVal avsändare som System.Object, _ ByVal e As System .EventArgs) _ Hanterar 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 (Av) Som System.Object, _ ByVal e As System.EventArgs) _ Hanterar knapp1.Klicka '-knapp 1 Rensa Array Dim en som ett heltal för a = 1 till 4 LabelArray (a) .Text = "" Nästa slut Sub Private Sub-knapp2_Click (_ ByVal avsändare som system.Objekt, _ ByVal e As system.EventArgs) _ Hanterar knapp2.Klicka 'Knapp 2 Fyll Array Dim a som heltal för en = 1 till 4 LabelArray (a) .Text = _ "Kontroll Array" & CStr ( a) Nästa slut Sub End Class
Om du experimenterar med den här koden kommer du att upptäcka att du förutom att ställa in egenskaper för etiketter också kan ringa metoder. Så varför gick jag (och Microsoft) till alla problem med att bygga den "fula" koden i del I av artikeln?
Jag måste hålla med om att det verkligen är ett "Control Array" i klassisk mening. VB 6 Control Array är en stödd del av VB 6-syntaxen, inte bara en teknik. Faktum är att kanske sättet att beskriva detta exempel är att det är en mängd kontroller, inte en kontrollgrupp.
I del I klagade jag över att Microsoft-exemplet KUN fungerade vid körtid och inte designtid. Du kan lägga till och ta bort kontroller från ett formulär dynamiskt, men det hela måste implementeras i kod. Du kan inte dra och släppa kontroller för att skapa dem som du kan i VB 6. Detta exempel fungerar huvudsakligen vid designtid och inte vid körningstid. Du kan inte lägga till och ta bort kontroller dynamiskt vid körning. På ett sätt är det hela motsatsen till del I-exemplet.
Det klassiska VB 6-kontrollarrayexemplet är detsamma som implementeras i VB .NET-koden. Här i VB 6-kod (detta är hämtat från Mezick & Hillier, Visual Basic 6 Certification Exam Guide, s 206 - något modifierat, eftersom exemplet i boken resulterar i kontroller som inte kan ses):
Dim MyTextBox som VB.TextBox Statisk intNumber som heltal intNumber = intNumber + 1 Ställ MyTextBox = _ Me.Controls.Add ("VB.TextBox", _ "Text" & intNumber) MyTextBox.Text = MyTextBox.Name MyTextBox.Name MyTextBox MyTextBox.Left = _ (intNumber - 1) * 1200
Men som Microsoft (och jag) håller med, är VB 6-kontrollmatriser inte möjliga i VB.NET. Så det bästa du kan göra är att kopiera funktionaliteten. Min artikel duplicerade funktionaliteten som finns i Mezick & Hillier-exemplet. Studiegruppskoden duplicerar funktionaliteten för att kunna ställa in egenskaper och samtalsmetoder.
Så slutet är att det verkligen beror på vad du vill göra. VB.NET har inte hela saken lindad som en del av språket - Ändå - men i slutändan är det mycket mer flexibelt.
John skrev: Jag behövde kontrollmatriser eftersom jag ville lägga en enkel tabell över siffror på ett formulär vid körning. Jag ville inte ha illamående att placera dem alla individuellt och jag ville använda VB.NET. Microsoft erbjuder en mycket detaljerad lösning på ett enkelt problem, men det är en väldigt stor slägga som knäcker en mycket liten mutter. Efter lite experiment försökte jag så småningom en lösning. Så här gjorde jag det.
Exemplet About Visual Basic ovan visar hur du kan skapa en TextBox på ett formulär genom att skapa en instans av objektet, ställa in egenskaper och lägga till den i kontrollsamlingen som är en del av formobjektet.
Dim txtData Visa som ny textbox
txtDataShow.Hight = 19
txtDataShow.Width = 80
txtDataShow.Location = Ny punkt (X, Y)
Me.Controls.Add (txtDataShow)
Även om Microsoft-lösningen skapar en klass, resonerade jag att det i stället skulle vara möjligt att förpacka allt detta i en subrutin. Varje gång du ringer till denna subrutin skapar du en ny instans av textrutan i formuläret. Här är den fullständiga koden:
Public Class Form1
Inherits System.Windows.Forms.Form
#Region "Windows Form Designer genererad kod"
Privat sub BtnStart_Click (_
ByVal avsändare som system.Objekt, _
ByVal e As System.EventArgs) _
Hanterar btnStart.Click
Dim I som heltal
Dim sData som sträng
För I = 1 till 5
sData = CStr (I)
Ring AddDataShow (sData, I)
Nästa
Avsluta under
Sub AddDataShow (_
ByVal sText As String, _
ByVal jag som heltal)
Dim txtData Visa som ny textbox
Dim UserLft, UserTop som heltal
Dim X, Y Som heltal
UserLft = 20
UserTop = 20
txtDataShow.Hight = 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 = Ny punkt (X, Y)
Me.Controls.Add (txtDataShow)
Avsluta under
Slutklass
Mycket bra poäng, John. Detta är säkert mycket enklare än Microsoft-koden ... så jag undrar varför de insisterade på att göra det på det sättet?
För att börja vår utredning, låt oss försöka ändra en av fastighetsuppgifterna i koden. Låt oss ändra
txtDataShow.Hight = 19
till
txtDataShow.Höjd = 100
bara för att se till att det finns en märkbar skillnad.
När vi kör koden igen får vi ... Whaaaat ??? ... samma sak. Ingen förändring alls. Faktum är att du kan visa värdet med ett uttalande som MsgBox (txtDataShow.Height) och du får fortfarande 20 som värdet på fastigheten oavsett vad du tilldelar den. Varför händer det?
Svaret är att vi inte härleder vår egen klass för att skapa föremål, vi lägger bara till saker i en annan klass så vi måste följa reglerna för den andra klassen. Och dessa regler säger att du inte kan ändra egenskapen Höjd. (Wellllll ... du kan. Om du ändrar egenskapen Multiline till True, kan du ändra höjden.)
Varför VB.NET går vidare och kör koden utan att ens en gnista att det kan vara något fel när det i själva verket helt bortser från ditt uttalande är en hel 'nother gripe. Jag kan dock föreslå åtminstone en varning i kompilering, dock. (Tips! Tips! Tips! Lyssnar Microsoft?)
Exemplet från del I ärver från en annan klass, och detta gör egenskaperna tillgängliga för koden i arvklassen. Att ändra egenskapen Höjd till 100 i detta exempel ger oss de förväntade resultaten. (Återigen ... en ansvarsfriskrivning: När en ny instans av en stor Label-komponent skapas täcker den den gamla. För att faktiskt se de nya Label-komponenterna måste du lägga till metoden som kallas aLabel.BringToFront ().)
Detta enkla exempel visar att även om vi KAN helt enkelt lägga till objekt i en annan klass (och ibland är det rätt att göra), kräver programmeringskontroll över objekten att vi härleder dem i en klass och på det mest organiserade sättet (vågar jag säga, ".NET-sättet" ??) är att skapa egenskaper och metoder i den nya härledda klassen för att ändra saker. John förblev övertygad till en början. Han sa att hans nya strategi passar hans syfte även om det finns begränsningar från att inte vara "COO" (korrekt objektorienterad). Senare skrev John dock,
"... efter att jag hade skrivit en uppsättning med 5 textlådor vid körning, ville jag uppdatera data i en efterföljande del av programmet - men ingenting förändrades - originaldata var fortfarande där.
Jag fann att jag kunde komma runt problemet genom att skriva kod för att ta bort de gamla rutorna och sätta tillbaka dem igen med ny data. Ett bättre sätt att göra det vore att använda Me.Refresh. Men detta problem har fått min uppmärksamhet för behovet av att tillhandahålla en metod för att subtrahera textrutorna och lägga till dem. "
Johns kod använde en global variabel för att hålla reda på hur många kontroller som hade lagts till i formuläret så en metod ...
Privat subform1_Load (_
ByVal avsändare som system.Objekt, _
ByVal e As System.EventArgs) _
Hanterar MyBase.Load
CntlCnt0 = Me.Controls.Count
Avsluta under
Sedan kan den "sista" kontrollen tas bort ...
N = Me.Controls.Count - 1
Me.Controls.RemoveAt (N)
John noterade att "kanske detta är lite klumpigt."
Det är så Microsoft håller reda på objekt i KOM OCH i deras "fula" exempelkod ovan.
Jag har nu återvänt till problemet med att dynamiskt skapa kontroller på ett formulär vid körning och jag har tittat igen på artiklarna "What Happened to Control Arrays".
Jag har skapat klasserna och kan nu placera kontrollerna på formen på det sätt jag vill att de ska vara.
John demonstrerade hur man kontrollerar placeringen av kontroller i en gruppruta med de nya klasserna han har börjat använda. Kanske hade Microsoft det rätt i sin "fula" lösning trots allt!