VB.NET Ce s-a întâmplat cu Arrayurile de control

Omiterea matricelor de control de la VB.NET este o provocare pentru cei care predau despre tablouri.

  • Nu mai este posibil să copiați pur și simplu un control, cum ar fi o cutie de text, și apoi să-l lipiți (o dată sau de mai multe ori) pentru a crea un tablou de control.
  • Codul VB.NET pentru crearea unei structuri similare cu un tablou de control a fost, în toate cărțile de pe VB.NET pe care le-am cumpărat și online, mult mai mult și mult mai complex. Îi lipsește simplitatea codificării unui tablou de control care se găsește în VB6.

Dacă faceți referire la biblioteca de compatibilitate VB6, există obiecte acolo care acționează cam ca tablourile de control. Pentru a vedea ce vreau să spun, pur și simplu folosiți expertul de upgrade VB.NET cu un program care conține un tablou de control. Codul este din nou urât, dar funcționează. Vestea proastă este că Microsoft nu va garanta că componentele de compatibilitate vor fi acceptate în continuare și nu trebuie să le utilizați.

Codul VB.NET pentru a crea și utiliza „tablele de control” este mult mai lung și mult mai complex.

Conform Microsoft, pentru a face ceva chiar mai aproape de ceea ce poți face în VB 6 necesită crearea unei „componente simple care să dubleze funcționalitatea tabloului de control”.

Aveți nevoie atât de o clasă nouă, cât și de un formular de găzduire pentru a ilustra acest lucru. Clasa creează și distruge noi etichete. Codul complet al clasei este următorul:

Clasa publică LabelArray
    Moștenesc System.Collections.CollectionBase
    Private ReadOnly HostForm As _
    System.Windows.Forms.Form
    Funcție publică AddNewLabel () _
    Ca System.Windows.Forms.Label
        'Creați o nouă instanță a clasei Label.
        Dim aLabel ca nou sistem.Windows.Forms.Label
        'Adăugați eticheta în colecția
    'listă internă.
        Me.List.Add (aLabel)
        'Adăugați eticheta în colecția Controale   
        'din formularul la care face referire câmpul HostForm.
        HostForm.Controls.Add (aLabel)
        'Setați proprietăți initiale pentru obiectul Label.
        aLabel.Top = Număr * 25
        aLabel.Width = 50
        aLabel.Left = 140
        aLabel.Tag = Me.Count
        aLabel.Text = "Etichetă" & Me.Count.ToString
        Returnați aLabel
    Funcția End
    Sub public nou (_
    Gazda ByVal ca System.Windows.Forms.Form)
        HostForm = gazdă
        Me.AddNewLabel ()
    Sub final
    Default Public ReadOnly Property _
        Articol (indexul ByVal ca număr întreg) ca _
        System.Windows.Forms.Label
        obține
            Returnare CType (Me.List.Item (Index), _
        System.Windows.Forms.Label)
        Încheiați
    Proprietate finală
    Subînlăturare publică ()
        „Verificați pentru a vă asigura că există o etichetă de eliminat.
        Dacă Me.Count> 0 Atunci
            'Eliminați ultima Etichetă adăugată în tablou 
            'din colecția de control de formular gazdă. 
        'Notă utilizarea proprietății implicite din 
            'accesarea tabloului.
            HostForm.Controls.Removezi (Me (Me.Count - 1))
            Me.List.RemoveAt (Me.Count - 1)
        End If
    Sub final
Clasa finală

Pentru a ilustra modul în care ar fi utilizat acest cod de clasă, puteți crea un Formular care îl apelează. Va trebui să folosiți codul prezentat mai jos în formularul:

Public Class Form1 Moștenește System.Windows.Forms.Form #Region "Codul de generare a formularului Windows" Codul generat "" De asemenea, trebuie să adăugați afirmația: "MyControlArray = New LabelArray (Me)" după apelul InitializeComponent () din "cod de regiune ascuns". 'Declarați un nou obiect ButtonArray. Dim MyControlArray As LabelArray Private Sub btnLabelAdd_Click (_ ByVal expeditor ca System.Object, _ ByVal e As System.EventArgs) _ Handles btnLabelAdd.Click 'Apelați metoda AddNewLabel' a MyControlArray. MyControlArray.AddNewLabel () 'Schimbă proprietatea BackColor' a butonului 0. MyControlArray (0) .BackColor = _ System.Drawing.Color.Red End Sub Private sub btnLabelRemove_Click (_ Expeditor ByVal ca System.Object, _ ByVal e As System) .EventArgs) _ Gestionează btnLabelRemove.Click 'Apelați metoda Eliminare a MyControlArray. MyControlArray.Remove () Clasa Sub End End

În primul rând, acest lucru nici măcar nu face treaba la Design Time, așa cum o făceam în VB 6! Și în al doilea rând, nu sunt într-un tablou, sunt într-o colecție VB.NET - un lucru mult diferit decât un tablou.

Motivul pentru care VB.NET nu acceptă „tabloul de control” al VB 6 este acela că nu există un „tablou” de „control” (rețineți schimbarea ghilimelelor). VB 6 creează o colecție din spatele scenei și o face să apară ca un tablou pentru dezvoltator. Dar nu este un tablou și ai prea puțin control asupra lui, dincolo de funcțiile furnizate prin IDE.

VB.NET, pe de altă parte, îl numește cum este: o colecție de obiecte. Și predau cheile regatului dezvoltatorului, creând totul în aer liber.

Ca un exemplu de tipul de avantaje pe care acesta îl oferă dezvoltatorului, în VB 6 controalele trebuiau să fie de același tip și trebuiau să aibă același nume. Deoarece acestea sunt doar obiecte în VB.NET, puteți să le faceți tipuri diferite și să le dați nume diferite și să le administrați în continuare în aceeași colecție de obiecte.

În acest exemplu, același eveniment Click gestionează două butoane și o casetă de selectare și afișează pe care a fost făcut clic. Faceți asta într-o linie de cod cu VB 6!

Sub-sub-mixt privatControls_Click (_
    Expeditor ByVal ca System.Object, _
    ByVal e As System.EventArgs) _
    Handles Button1.Click, _
            Button2.Faceți clic, _
            CheckBox1.Click
    „Declarația de mai jos trebuie să fie o declarație lungă!
    - Este pe patru rânduri aici pentru a-l îngusta
    'suficient pentru a se potrivi pe o pagină web
    Etichetă2.Text = 
    Microsoft.VisualBasic.Right (sender.GetType.ToString, 
    Len (sender.GetType.ToString) - 
    (InStr (sender.GetType.ToString, "Formulare") + 5))
Sub final

Calculul stratului este complex, dar nu despre ce vorbim aici. Ați putea face orice în cadrul evenimentului Click. Puteți folosi, de exemplu, tipul controlului într-o instrucțiune If pentru a face lucruri diferite pentru controale diferite.

Grupul lui Frank's Computing Studies Feedback on Srays

Grupul de studiu al lui Frank a oferit un exemplu cu un formular care are 4 etichete și 2 butoane. Butonul 1 șterge etichetele și butonul 2 le umple. Este o idee bună să citiți din nou întrebarea inițială a lui Frank și să observați că exemplul pe care l-a folosit a fost o buclă care este folosită pentru a șterge proprietatea Titlu pentru o serie de componente Label. Iată echivalentul VB.NET al codului VB 6. Acest cod face ceea ce Frank a cerut inițial!

Public Class Form1 Inherits System.Windows.Forms.Form #Region "Windows Form Designer generat cod" Dim LabelArray (4) As Label 'declară o serie de etichete Private Sub Form1_Load (_ expeditor ByVal ca 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 Sub Button1_Clicer As System.Object, _ ByVal e As System.EventArgs) _ Handles Button1.Click 'Button 1 Clear Array Dim a As Integer For a = 1 To 4 LabelArray (a) .Text = "" Next End Sub Private Sub Button2_Click (_ ByVal expeditor As System.Object, _ ByVal e As System.EventArgs) _ Handles Button2.Click 'Button 2 Completare Array Dim a As Integer For a = 1 To 4 LabelArray (a) .Text = _ "Control Array" & CStr ( a) Următoarea clasă finală

Dacă experimentezi cu acest cod, vei descoperi că pe lângă setarea proprietăților Etichetelor, poți apela și la metode. Deci, de ce am (și Microsoft) am trecut la toate problemele pentru a construi codul „Urât” din partea I a articolului?

Nu trebuie să fiu de acord că este într-adevăr un „control Array” în sensul clasic VB. VB 6 Control Array este o parte acceptată a sintaxei VB 6, nu doar o tehnică. De fapt, poate că modalitatea de a descrie acest exemplu este că este o serie de controale, nu o matrice de control.

În partea I, m-am plâns că exemplul Microsoft a funcționat DOAR la timpul de rulare și nu la timpul de proiectare. Puteți adăuga și șterge controale dintr-un formular în mod dinamic, dar totul trebuie să fie implementat în cod. Nu puteți trage și arunca controale pentru a le crea așa cum puteți în VB 6. Acest exemplu funcționează în principal la timpul de proiectare și nu la timpul de rulare. Nu puteți adăuga și șterge controalele în mod dinamic în timpul rulării. Într-un fel, este opusul complet al exemplului din partea I.

Exemplul clasic al tabloului de control VB 6 este același care este implementat în codul VB .NET. Aici în cod VB 6 (acesta este preluat de la Mezick & Hillier, Ghidul examenului de certificare Visual Basic 6, p 206 - ușor modificat, deoarece exemplul din carte are ca rezultat controale care nu pot fi văzute):

Dim MyTextBox ca VB.TextBox Static IntNumber as Integer intNumber = intNumber + 1 Set MyTextBox = _ Me.Controls.Add ("VB.TextBox", _ "Text" și intNumber) MyTextBox.Text = MyTextBox.Name MyVextible MyTextBox.Left = _ (numărul int - 1) * 1200

Dar, deoarece Microsoft (și eu) suntem de acord, tablourile de control VB 6 nu sunt posibile în VB.NET. Deci, cel mai bun lucru pe care îl puteți face este duplicarea funcționalității. Articolul meu a duplicat funcționalitatea găsită în exemplul Mezick & Hillier. Codul Grupului de studiu duplică funcționalitatea de a putea seta proprietăți și metode de apel.

Deci, linia de bază este că depinde cu adevărat de ceea ce vrei să faci. VB.NET nu are totul învelit ca parte a limbii - Cu toate acestea, dar în cele din urmă este mult mai flexibil.

Piesele de control ale lui John Fannon

John a scris: Aveam nevoie de tablouri de control pentru că voiam să pun o simplă tabelă de numere pe un formular la timpul de execuție. Nu voiam greața de a le pune pe toate individual și am vrut să folosesc VB.NET. Microsoft oferă o soluție foarte detaliată la o problemă simplă, dar este un trântor foarte mare pentru a sparge o piuliță foarte mică. După câteva experimentări, am ajuns în cele din urmă la o soluție. Iată cum am făcut-o.

Exemplul Despre Visual Basic de mai sus arată cum puteți crea un TextBox pe un formular prin crearea unei instanțe a obiectului, setarea proprietăților și adăugarea acesteia în colecția Controls care face parte din obiectul Form.

Dim txtDataShow Ca New TextBox
txtDataShow.Height = 19
txtDataShow.Width = 80
txtDataShow.Location = Punct nou (X, Y)
Me.Controls.Add (txtDataShow)
Deși soluția Microsoft creează o clasă, am motivat că ar fi posibil să înfășurați toate acestea într-o subrutină. De fiecare dată când apelați această subrutină creați o nouă instanță a căsuței de text din formular. Iată codul complet:

Clasa publică Form1
    Moștenesc System.Windows.Forms.Form

#Region „Codul generat de Windows Form Designer”

    Subcartul privat BtnStart_Click (_
        Expeditor ByVal ca System.Object, _
        ByVal e As System.EventArgs) _
        Manere btnStart.Click

        Dim I ca număr întreg
        Dim sData ca șir
        Pentru I = 1 până la 5
            sData = CStr (I)
            Apelați AddDataShow (sData, I)
        Următor →
    Sub final
    SubAdăugareDatașă (_
        ByVal sText As String, _
        ByVal I ca întreg)

        Dim txtDataShow Ca New TextBox
        Dim UserLft, UserTop Ca număr întreg
        Dim X, Y Ca număr întreg
        UserLft = 20
        UserTop = 20
        txtDataShow.Height = 19
        txtDataShow.Width = 25
        txtDataShow.TextAlign = _
            HorizontalAlignment.Center
        txtDataShow.BorderStyle = _
            BorderStyle.FixedSingle
        txtDataShow.Text = sText
        X = Flux de utilizator
        Y = UserTop + (I - 1) * txtDataShow.Height
        txtDataShow.Location = Punct nou (X, Y)
        Me.Controls.Add (txtDataShow)
    Sub final
Clasa finală
Foarte bine, John. Acest lucru este cu siguranță mult mai simplu decât codul Microsoft ... așa că mă întreb de ce au insistat să o facă în acest fel?

Pentru a începe investigația noastră, să încercăm să schimbăm una dintre atribuțiile de proprietate din cod. Hai sa schimbam

txtDataShow.Height = 19
la

txtDataShow.Height = 100
doar pentru a vă asigura că există o diferență notabilă.

Când rulăm din nou codul, primim ... Whaaaat ??? ... același lucru. Nicio schimbare deloc. De fapt, puteți afișa valoarea cu o afirmație precum MsgBox (txtDataShow.Height) și veți primi totuși 20 ca valoare a proprietății indiferent de ce i-ați atribuit. De ce se întâmplă asta??

Răspunsul este că nu derivăm propria noastră clasă pentru a crea obiectele, doar adăugăm lucruri la o altă clasă, așa că trebuie să respectăm regulile celeilalte clase. Iar aceste reguli afirmă că nu puteți schimba proprietatea Height. (Wellllll ... puteți. Dacă schimbați proprietatea Multiline în True, atunci puteți modifica Înălțimea.)

De ce VB.NET merge înainte și execută codul, fără să știe măcar că ar putea să se întâmple ceva în neregulă atunci când, de fapt, nu ține cont total de afirmația dvs. este un cu totul altceva. Aș putea sugera cel puțin un avertisment în compilare, totuși. (Hint! Hint! Hint! Microsoft ascultă?)

Exemplul din partea I moștenește de la o altă clasă și acest lucru face ca proprietățile să fie disponibile codului din clasa de moștenire. Modificarea proprietății înălțimii la 100 în acest exemplu ne oferă rezultatele așteptate. (Din nou ... o exonerare: Când este creată o nouă instanță a unei componente Label mari, aceasta o acoperă pe cea veche. Pentru a vedea efectiv noile componente Label, trebuie să adăugați metoda apelul aLabel.BringToFront ().)

Acest exemplu simplu arată că, deși putem să adăugăm pur și simplu obiecte într-o altă clasă (și uneori acesta este ceea ce trebuie să facem), programarea controlului asupra obiectelor necesită să le derivăm într-o clasă și în cel mai organizat mod (îndrăznesc să spun, "modul .NET" ??) este de a crea proprietăți și metode din noua clasă derivată pentru a schimba lucrurile. John a rămas la început neconvins. El a spus că noua lui abordare se potrivește scopului său, chiar dacă există limitări de a nu fi „COO” (corect orientat pe obiecte). Mai recent, însă, John a scris,

„… După ce am scris un set de 5 cutii de text la runtime, am vrut să actualizez datele dintr-o parte ulterioară a programului - dar nimic nu s-a schimbat - datele originale erau încă acolo.

Am descoperit că pot rezolva problema scriind cod pentru a scoate casetele vechi și a le reface cu date noi. Un mod mai bun de a face acest lucru ar fi să folosești Me.Refresh. Dar această problemă mi-a atras atenția asupra necesității de a furniza o metodă pentru a scădea cutiile de text, precum și pentru a le adăuga. "

Codul lui John a folosit o variabilă globală pentru a urmări câte controale au fost adăugate în formular, astfel încât o metodă ...

Sub Formular privat1_Load (_
   Expeditor ByVal ca System.Object, _
   ByVal e As System.EventArgs) _
   Manerele MyBase.Load
   CntlCnt0 = Me.Controls.Count
Sub final

Apoi, ultimul control ar putea fi eliminat ...

N = Me.Controls.Count - 1
Me.Controls.RemoveAt (N)
John a menționat că, „poate este un pic stângaci”.

Este modul în care Microsoft ține evidența obiectelor în COM ȘI în codul de exemplu „urât” de mai sus.

Am revenit acum la problema creării dinamice a controalelor pe un formular în timpul rulării și m-am uitat din nou la articolele „Ce s-a întâmplat să controlăm”.

Am creat clasele și acum pot plasa controalele pe formular în modul în care vreau să fie.

Ioan a demonstrat cum să controleze plasarea controalelor într-o casetă de grup folosind noile clase pe care a început să le utilizeze. Poate că Microsoft a avut-o chiar în soluția lor „urâtă” până la urmă!