VB.NET: Hva skjedde med kontrollarrayer

Hvordan håndtere samlinger av kontroller i VB.NET

Utelatelsen av kontrollmatriser fra VB.NET er en utfordring for de som lærer om matriser.





  • Det er ikke lenger mulig å kopiere en kontroll, for eksempel en tekstboks, og deretter lime den inn (en eller flere ganger) for å lage en kontrollarray.
  • VB.NET-koden for å lage en struktur som ligner på en kontrollarray har vært, i alle bøkene på VB.NET som jeg har kjøpt og på nettet, mye lengre og mye mer kompleks. Den mangler enkelheten ved å kode en kontrollarray som finnes i VB6.

Hvis du refererer til VB6-kompatibilitetsbiblioteket, er det objekter der inne som fungerer omtrent som kontrollarrayer. For å se hva jeg mener, bruk bare VB.NET oppgraderingsveiviseren med et program som inneholder en kontrollarray. Koden er stygg igjen, men den fungerer. Den dårlige nyheten er at Microsoft ikke vil garantere at kompatibilitetskomponentene vil fortsette å støttes, og det er ikke meningen at du skal bruke dem.

VB.NET-koden for å lage og bruke 'kontrollmatriser' er mye lengre og mye mer kompleks.



Ifølge Microsoft, for å gjøre noe i nærheten av det du kan gjøre i VB 6, kreves det at du oppretter en 'enkel komponent som dupliserer kontrollarray-funksjonalitet.'

Du trenger både en ny klasse og et vertsskjema for å illustrere dette. Klassen oppretter og ødelegger faktisk nye etiketter. Den komplette klassekoden er som følger:



Public Class LabelArray
Arver System.Collections.CollectionBase
Privat skrivebeskyttet vertsskjema som _
System.Windows.Forms.Form
Offentlig funksjon AddNewLabel() _
Som System.Windows.Forms.Label
' Opprett en ny forekomst av Label-klassen.
Dim aLabel As New System.Windows.Forms.Label
' Legg etiketten til samlingens
' intern liste.
Me.List.Add(aLabel)
' Legg til etiketten i kontrollsamlingen
' av skjemaet det refereres til i HostForm-feltet.
HostForm.Controls.Add(aLabel)
' Angi innledende egenskaper for Label-objektet.
aLabel.Top = Antall * 25
aLabel.Width = 50
aLabel.Left = 140
aLabel.Tag = Me.Count
aLabel.Text = 'Etikett ' & Me.Count.ToString
Returner enLabel
Avslutt funksjon
Offentlig under ny( _
ByVal host As System.Windows.Forms.Form)
HostForm = vert
Me.AddNewLabel()
End Sub
Standard offentlig skrivebeskyttet egenskap _
Element(ByVal Index As Integer) As _
System.Windows.Forms.Label

Returner CType(Me.List.Item(Index), _
System.Windows.Forms.Label)
Slutt Get
Avslutt eiendom
Offentlig sub Fjern()
' Sjekk for å være sikker på at det er en etikett å fjerne.
Hvis Me.Count > 0 Da
' Fjern den siste etiketten som ble lagt til i matrisen
' fra vertsskjemakontrollsamlingen.
' Legg merke til bruken av standardegenskapen i
' tilgang til matrisen.
HostForm.Controls.Remove(Me(Me.Count - 1))
Me.List.RemoveAt(Me.Count - 1)
Slutt om
End Sub
Slutt klasse

For å illustrere hvordan denne klassekoden vil bli brukt, kan du lage et skjema som kaller den. Du må bruke koden vist nedenfor i skjemaet:

|_+_|

For det første gjør dette ikke engang jobben på Design Time som vi pleide å gjøre det i VB 6! Og for det andre, de er ikke i en matrise, de er i en VB.NET-samling - en mye annerledes ting enn en matrise.

Grunnen til at VB.NET ikke støtter VB 6 'kontroll array' er at det ikke er noe slikt som en 'kontroll' 'array' (merk endringen i anførselstegn). VB 6 lager en samling bak kulissene og får den til å fremstå som en rekke for utvikleren. Men det er ikke en matrise, og du har liten kontroll over den utover funksjonene som tilbys gjennom IDE.



VB.NET kaller det derimot hva det er: en samling av objekter. Og de gir nøklene til kongeriket til utvikleren ved å lage det hele rett ut i det åpne.

Som et eksempel på hva slags fordeler dette gir utvikleren, i VB 6 måtte kontrollene være av samme type, og de måtte ha samme navn. Siden disse bare er objekter i VB.NET, kan du gjøre dem til forskjellige typer og gi dem forskjellige navn og fortsatt administrere dem i samme samling av objekter.



I dette eksemplet håndterer den samme klikkhendelsen to knapper og en avkrysningsboks og viser hvilken som ble klikket på. Gjør det i én kodelinje med VB 6!

Private Sub MixedControls_Click( _
ByVal sender Som System.Object, _
ByVal e As System.EventArgs) _
Håndterer knapp 1. Klikk, _
Knapp 2. Klikk, _
Avmerkingsboks 1. Klikk
' Utsagnet nedenfor må være ett langt utsagn!
' Den er på fire linjer her for å holde den smal
' nok til å passe på en nettside
Etikett2.Tekst =
Microsoft.VisualBasic.Right(sender.GetType.ToString,
Len(sender.GetType.ToString) -
(InStr(sender.GetType.ToString, 'Forms') + 5))
End Sub



Delstrengberegningen er litt kompleks, men det er egentlig ikke det vi snakker om her. Du kan gjøre hva som helst i Click-arrangementet. Du kan for eksempel bruke typen av kontrollen i en If-setning til å gjøre forskjellige ting for forskjellige kontroller.

Frank's Computing Studies Group Feedback on Arrays

Franks studiegruppe ga et eksempel med et skjema som har 4 etiketter og 2 knapper. Knapp 1 sletter etikettene og knapp 2 fyller dem. Det er en god idé å lese Franks originale spørsmål igjen og legge merke til at eksemplet han brukte var en løkke som brukes til å fjerne Caption-egenskapen til en rekke etikettkomponenter. Her er VB.NET-ekvivalenten til den VB 6-koden. Denne koden gjør det Frank opprinnelig ba om!



|_+_|

Hvis du eksperimenterer med denne koden, vil du oppdage at i tillegg til å angi egenskaper for etikettene, kan du også kalle metoder. Så hvorfor gjorde jeg (og Microsoft) all bryet med å bygge den 'stygge' koden i del I av artikkelen?

Jeg må være uenig i at det egentlig er en 'Control Array' i klassisk VB-forstand. VB 6 Control Array er en støttet del av VB 6-syntaksen, ikke bare en teknikk. Faktisk er kanskje måten å beskrive dette eksemplet på at det er en rekke kontroller, ikke en kontrollmatrise.

I del I klaget jeg over at Microsoft-eksemplet BARE fungerte på kjøretid og ikke designtid. Du kan legge til og slette kontroller fra et skjema dynamisk, men det hele må implementeres i kode. Du kan ikke dra og slippe kontroller for å lage dem slik du kan i VB 6. Dette eksemplet fungerer hovedsakelig ved design og ikke under kjøretid. Du kan ikke legge til og slette kontroller dynamisk under kjøring. På en måte er det helt motsatt av del I-eksemplet.

Det klassiske VB 6-kontrollarrayeksemplet er det samme som er implementert i VB .NET-koden. Her i VB 6-kode (dette er hentet fra Mezick & Hillier, Visual Basic 6 sertifiseringseksamenveiledning , s 206 - litt modifisert, siden eksemplet i boken resulterer i kontroller som ikke kan sees):

|_+_|

Men som Microsoft (og jeg) er enige om, er VB 6-kontrollarrayer ikke mulige i VB.NET. Så det beste du kan gjøre er å duplisere funksjonaliteten. Artikkelen min dupliserte funksjonaliteten i Mezick & Hillier-eksemplet. Studiegruppekoden dupliserer funksjonaliteten for å kunne sette egenskaper og anropsmetoder.

Så poenget er at det virkelig avhenger av hva du vil gjøre. VB.NET har ikke hele greia pakket inn som en del av språket -- ennå -- men til syvende og sist er det langt mer fleksibelt.

John Fannons Take on Control Arrays

John skrev: Jeg trengte kontrollmatriser fordi jeg ønsket å sette en enkel tabell med tall på et skjema under kjøring. Jeg ville ikke ha kvalmen av å plassere dem alle individuelt, og jeg ønsket å bruke VB.NET. Microsoft tilbyr en veldig detaljert løsning på et enkelt problem, men det er en veldig stor slegge å knekke en veldig liten mutter. Etter litt eksperimentering fant jeg til slutt en løsning. Her er hvordan jeg gjorde det.

Om Visual Basic-eksemplet ovenfor viser hvordan du kan lage en tekstboks på et skjema ved å opprette en forekomst av objektet, angi egenskaper og legge det til kontrollsamlingen som er en del av skjemaobjektet.

Dim txtDataShow As New TextBox
txtDataShow.Height = 19
txtDataShow.Width = 80
txtDataShow.Location = Nytt punkt(X, Y)
Me.Controls.Add(txtDataShow)
Selv om Microsoft-løsningen oppretter en klasse, tenkte jeg at det ville være mulig å pakke alt dette inn i en subrutine i stedet. Hver gang du kaller denne subrutinen oppretter du en ny forekomst av tekstboksen på skjemaet. Her er hele koden:

Offentlig klasseskjema1
Arver System.Windows.Forms.Form

#Region ' Windows Form Designer generert kode '

Private Sub BtnStart_Click( _
ByVal sender Som System.Object, _
ByVal e As System.EventArgs) _
Håndterer btnStart.Click

Dim I som heltall
Dim sData som streng
For I = 1 til 5
sData = CStr(I)
Ring AddDataShow(sData, I)
Neste
End Sub
Sub AddDataShow( _
ByVal sText As String, _
ByVal I som heltall)

Dim txtDataShow As New TextBox
Dim UserLft, UserTop Som heltall
Dim X, Y Som heltall
UserLft = 20
BrukerTopp = 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 = Nytt punkt(X, Y)
Me.Controls.Add(txtDataShow)
End Sub
Slutt klasse
Veldig godt poeng, John. Dette er absolutt mye enklere enn Microsoft-koden ... så jeg lurer på hvorfor de insisterte på å gjøre det på den måten?

For å starte undersøkelsen vår, la oss prøve å endre en av eiendomstildelingene i koden. La oss endre

txtDataShow.Height = 19
til

txtDataShow.Height = 100
bare for å være sikker på at det er en merkbar forskjell.

Når vi kjører koden igjen får vi ... Whaaaat??? ... det samme. Ingen endring i det hele tatt. Faktisk kan du vise verdien med en setning som MsgBox (txtDataShow.Height), og du får fortsatt 20 som verdien av eiendommen uansett hva du tildeler den. Hvorfor skjer det?

Svaret er at vi ikke utleder vår egen klasse for å lage objektene, vi legger bare til ting i en annen klasse, så vi må følge reglene for den andre klassen. Og disse reglene sier at du ikke kan endre egenskapen Høyde. (Velllll ... du kan. Hvis du endrer Multiline-egenskapen til True, kan du endre høyden.)

Hvorfor VB.NET går videre og kjører koden uten engang et klynk om at det kan være noe galt, når det faktisk ignorerer utsagnet ditt totalt, er en helt annen klage. Jeg kan imidlertid foreslå i det minste en advarsel i kompileringen. (Hint! Hint! Hint! Hører Microsoft etter?)

Eksemplet fra del I arver fra en annen klasse, og dette gjør egenskapene tilgjengelige for koden i den arvende klassen. Å endre Height-egenskapen til 100 i dette eksemplet gir oss de forventede resultatene. (Igjen ... en ansvarsfraskrivelse: Når en ny forekomst av en stor Label-komponent er opprettet, dekker den opp den gamle. For å faktisk se de nye Label-komponentene, må du legge til metodekallet aLabel.BringToFront().)

Dette enkle eksemplet viser at selv om vi ganske enkelt KAN legge til objekter til en annen klasse (og noen ganger er dette den rette tingen å gjøre), krever programmeringskontroll over objektene at vi utleder dem på en klasse og den mest organiserte måten (tør jeg si, 'the .NET way' ??) er å lage egenskaper og metoder i den nye avledede klassen for å endre ting. John forble ikke overbevist til å begynne med. Han sa at hans nye tilnærming passer til formålet hans, selv om det er begrensninger fra å ikke være 'COO' (Correctly Object Oriented). Nylig skrev imidlertid John,

' ... etter å ha skrevet et sett med 5 tekstbokser under kjøring, ønsket jeg å oppdatere dataene i en påfølgende del av programmet - men ingenting endret seg - de originale dataene var fortsatt der.

Jeg fant ut at jeg kunne omgå problemet ved å skrive kode for å ta av de gamle boksene og sette dem tilbake igjen med nye data. En bedre måte å gjøre det på er å bruke Me.Refresh. Men dette problemet har trukket min oppmerksomhet for behovet for å levere en metode for å trekke fra tekstboksene og legge dem til.'

Johns kode brukte en global variabel for å holde styr på hvor mange kontroller som var lagt til skjemaet, så en metode ...

Privat underskjema1_Load( _
ByVal sender Som System.Object, _
ByVal e As System.EventArgs) _
Håndterer MyBase.Load
CntlCnt0 = Me.Controls.Count
End Sub

Da kan den 'siste' kontrollen fjernes ...

N = Me.Controls.Count - 1
Me.Controls.RemoveAt(N)
John bemerket at 'kanskje dette er litt klønete.'

Det er måten Microsoft holder styr på objekter i COM OG i deres 'stygge' eksempelkode ovenfor.

Jeg har nå gått tilbake til problemet med å dynamisk lage kontroller på et skjema under kjøretid, og jeg har sett igjen på artiklene 'Hva skjedde med kontrollarrayer'.

Jeg har opprettet klassene og kan nå plassere kontrollene på skjemaet slik jeg vil at de skal være.

John demonstrerte hvordan man kontrollerer plassering av kontroller i en gruppeboks ved å bruke de nye klassene han har begynt å bruke. Kanskje Microsoft hadde rett i sin 'stygge' løsning tross alt!