Hoofdstuk 14 Klassen (Deel 2) In vorig hoofdstuk hebben we al een belangrijk deel gezien over het gebruik van klassen in VB.net. we hebben al klassen gezien, hoe we met hun eigenschappen (property) werken en hoe we methoden in deze klassen werkten. Echter waren het methoden zonder parameters. In dit gedeelte zullen we tonen dat het niet altijd nodig is om eerst eigenschappen aan te maken en vervolgens waarden aan toe te kennen, om deze te gebruiken in methoden.
14.1 gebruik van de tabcontrol Voor we beginnen aan de eerste oefening gaan we eerst even het TAB-control element bij een formulier uitleggen. We voegen de property tabcontrol toe op een nieuw formulier we zien dat er automatisch 2 tabbladen worden aangemaakt. Om deze tabbladen te benoemen gaan we in de eigenschappen van deze control bij het onderwerp TabPages
Fig. 1
Na het drukken op deze krijgen we onderstaande figuur:
Fig. 2
Waarbij we in de eigenschap Text de verschillende namen van onze tabbladen invoegen. Uiteindelijk krijgen we dan
rd
Fig. 3
De namen van deze tabs zijn respectievelijke tabCirkel en tabRecht. Let wel dat we de tabcontrol de naam geven tbcFiguren. Maak dan de rest van de form op volgens de onderstaande vorm waarbij we erop letten dat we de verschillende objecten juist bepalen.
Fig. 4
2
rd Bij het maken van bovenstaande figuur, let er op dat we een picturebox (pctKleur) gebruiken waar we de background instellen op Rood (property), en voor de andere picturebox (pctFiguur) we de background op wit houden. Tevens zorgen we dat we een kleur dialoog toevoegen op de gebruikelijke wijze. Namelijk in dialoog, kleur dialoog aanduiden en verslepen naar onze form. De dialoog zal onderaan het form verschijnen. We geven hem als naam: diaKleur.
14.2 oplossen van de oefening De uiteindelijke bedoeling van deze oefening is een klasse te maken die: Op een gegeven object een figuur tekent. De afstand van de linker en de bovenmarge regelt de kleur warmede getekend wordt is dezelfde als de achtengrondkleur, deze kan dan uiteindelijk geregeld worden door middel van de kleurendialoog in samenwerking met de knop “lijnkleur”. Deze wordt dan bepaald door de dialoog. Tevens wordt de oppervlakte en de omtrek van de figuur bepaald. 14.2.1 maken van de klasse In eerste instantie zullen we een klasse invoegen (op de gebruikelijke wijze). Deze klasse noemen we “vormen”. Onderstaande code komt in deze klasse: Public Class Vormen Private PI As Single = Math.PI Public Sub TekenCirkel(ByVal Canvas As Object, ByVal Kleur As Color, _ ByVal Straal As Single, ByVal X As Single, _ ByVal Y As Single) Dim Omtrek, Opp As Single Dim Diameter As Single = Straal * 2 Omtrek = Straal * PI * 2 Opp = Straal * Straal * PI Dim Potlood As New Drawing.Pen(Kleur) Dim Borstel As New SolidBrush(Kleur) Dim Paneel As Drawing.Graphics Dim MyFont = New Font("Arial", 10, FontStyle.Italic) Paneel = Canvas.CreateGraphics Paneel.Clear(Color.White) Paneel.DrawString("Omtrek = " + Omtrek.ToString, MyFont, Borstel, 0, 0) Paneel.DrawString("Oppervlakte = " + Opp.ToString, MyFont, Borstel, 0, 10) Paneel.DrawEllipse(Potlood, X, Y, Straal * 2, Straal * 2) Borstel.Dispose() Potlood.Dispose() Paneel.Dispose() End Sub
3
rd End Class
Bij de verschillende knoppen zullen we onderstaande code voegen: Knop lijnkleur: Private Sub btnKleur_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnKleeur.Click diaKleur.Color = pctKleur.BackColor If diaKleur.ShowDialog = DialogResult.OK Then pctKleur.BackColor = diaKleur.Color End If
Knop TekenCirkel: Private Sub btnTeknCirkel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnTekenCirkel.Click Dim C As New Vormen C.TekenCirkel(picFiguur, pctKleur.BackColor, Single.Parse(txtStraal.Text), _ Single.Parse(txtLinks.Text), Single.Parse(txtBoven.Text))
In feite is dit een kleine oefening, al het programmeerwerk gebeurt (zoals het zou moeten) in de klasse. Laten we de werking even bekijken: In de klasse zelf zijn er maar 2 objecten aanwezig - een private constante PI (let op binnen de klasse is die publiek) - een methode TekenCirkel die 5 parameters nodig heeft met name 1. Het Canvas komt een object waarop kan getekend worden 2. De kleur waarin getekend zal worden 3. De straal van de cirkel 4. De X afstand 5. De Y afstand Om de figuur te tekenen hebben we een ander object nodig die we hier potlood noemen van het type Drawing.Pen. Als we deze declareren geven we direct de kleur mee waarin we gaan tekenen. Om de tekst uiteindelijk af te drukken heb je nog een object nodig van het type Drawing.SolidBrush. Deze noemen we hier borstel. Eveneens hier geven we de kleur mee waarin de tekst moet geschreven worden.
4
rd Het object waarop getekend moet worden is van het type Drawing.Graphics en noemt hier Paneel. Met de declaratie van MyFont zullen we door het Font type te gebruiken de tekst opmaken. We gaan ook duidelijk maken dat we met paneel eigenlijk een object bedoelen die als parameter wordt doorgegeven (paneel = Canva.CreateGraphics). We zorgen echter ook dat het paneel gewist wordt voordat er op getekend wordt, We geven hiervoor de kleur op waarmede wordt gewist. Eerst schrijven we de omtrek en de oppervlakte op het paneel en dan gaan we de figuur tekenen. Tenslotte mogen we niet vergeten het object variabelen op het einde vrij te geven. Dit gebeurt met de dispose() methode (zie later). Er rest nog enkel de figuur te tekenen met de knop teken cirkel in te drukken.
Fig. 5
Als oefening probeert even het probleem met de rechthoek.
5
rd
Oefening 2 Bij deze oefening wordt er van u gevraagd om een wiel met een vrij aantal te bepalen spaken te tekenen. Om dit tot een goed einde te brengen zijn er echter een paar zaken waar je rekening moet mee houden, zoals driehoeksmeting. Stel we werken met en cirkel met een straal van 100 (S = 100) dan geld dat: a = cos(30°) * S b = sin(30°) * S zie tekening
6
rd
s = 100 b=? a=?
Er is echter een klein probleem. De functie sin en cos zijn bekend in VB.net echter werken die enkel met Radialen in de plaats van graden. Om deze te berekenen hebben we de formule: ·Radialen = (Graden * PI)/180.
7
rd
14.3 maken van tabellen van een klasse Zoals in bij alle gewone variabelen is het ook mogelijk van tabellen te maken van klassen. Neem aan dat we met behulp van een variabel C een instantie van een klasse maken bvb Dim C as new klasseNaam dan spreken we van een object. In dit geval is c een object en niet meer een klasse. Dus in feite maken we hier een tabel van objecten. We moeten dat nu echter wel organiseren in twee stappen: Dim tabel(2) as klasseNaam tabel(0) as new klasseNaam tabel(1) as new klasseNaam tabel(3) as new klasseNaam Merk op dat we de eigenschappen van een tabel, steeds beginnen van 0 ook hier gebruiken. Immers een tabel blijft een tabel ook voor objecten. We kunnen natuurlijk ook een lus gebruiken om de tabel te vullen. Dan krijgen we: Dim teller as Integer Dim tabel(2) as klasseNaam For teller = 0 to 2 tabel(teller) = new klasseNaam next 14.3.1 herdimensioneren van een tabel Bij het maken van tabellen hebben we niet speciaal door rekening gehouden met het dimensioneren van de tabel en de mogelijkheden. Tijdens de code is het immers mogelijk om een bestaande tabel in grote te veranderen. We gebruiken daarvoor het commando Redim [Preserve] tabel(tabelgrote) Redim: herdimensioneren van de variabele tabel Perserve (optioneel) wordt gebruikt om de bestaande data in de tabel te bewaren bij het herdimensioneren. tabel(tabelgrote) de tabelnaam (is de oorspronkelijke naam) met de nieuwe grote van de tabel. Een voorbeeld fig. 78 zal dit illustreren: Hierin gaan we een tabel van 3 elementen maken en opvullen met de getallen 0 tot en met 2. Naderhand zullen we dezelfde tabel, herdimensioneren en terug vullen met andere gegevens met en zonder bewaren van de eerste gegevens.
8
rd
Fig. 6
Bij behorende code: Public Class Form1 Dim tabel(2) As Integer Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim x, y As Integer For x = 0 To 2 tabel(x) = x ListBox1.Items.Add(tabel(x)) Next End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click ReDim Preserve tabel(4) tabel(3) = 9 tabel(4) = 10 For x = 0 To 4 ListBox2.Items.Add(tabel(x)) Next End Sub Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click ReDim tabel(4) tabel(3) = 9 tabel(4) = 10 For x = 0 To 4 ListBox3.Items.Add(tabel(x)) Next End Sub End Class
Merk op dat we 1 maal de tabel bepaald hebben in het begin van het ptrogramma, doch dat we steeds die tabel blijven gebruiken. Hij moet echter van hetzelfde type blijven. 9
rd 14.3.2 voorbeeld: We gaan een lijst maken van namen voornamen en geboortedatum en gemeente waar ze wonen van de leden van onze klas.
Fig. 7
In bovenstaand voorbeeld zal men gebruikmaken van bepaalde mogelijkheden welke we al kennen. We hebben de groepbox gegevens in het maken gedisabeld ( Enable = False). Daardoor kunnen we de tekstvakken niet invullen. Als de gebruiker op Add drukt in de groep gegevens zullen we deze vrij geven zodat we onze tabel kunnen opvullen en de knoppen Bewaar en Cancel ook kunnen gebruikt worden. Op dit moment echter wordt de andere groep ontoegankelijk gemaakt met de zelfde methode. Door het drukken op deze worden ofwel de gegevens bewaard of wel gewist. Bij het drukken op één van deze knoppen zal terug de oorspronkelijke opstelling komen (let wel de tekstboxen worden leeg gemaakt), detail onbereikbaar gegevens bereikbaar. Als men op Save gedrukt heeft zullen de gegevens natuurlijk verschijnen in de listbox lstGegevens. Als men op Edit drukt zal het geselecteerde veld verschijnen in de tekstboxen waar men iets kan veranderen en terug bewaren. Op de 10
rd knop Del zal het geselecteerde gegeven verwijderd worden. Dit lijkt ingewikkeld maar door gebruik van klassen zal dit betrekkelijk eenvoudig worden. 14.3.2.1 code: Vooreerst maken we een klasse aan met in te vullen gegevens. Deze gegevens zijn degene dier we aanmaken in onze form via de tekst boxen. We gebruiken een klasse omdat de vraag naar gegevens terugkomt, en voor de goede werking we er een tabel gaan van maken. Public Class gegevens Private prNaam As String Private prVoorNaam As String Private prGeboorteDatum As String Private prGemeente As String Public Property Naam() As String Get Naam = prNaam End Get Set(ByVal value As String) prNaam = value End Set End Property Public Property VoorNaam() As String Get VoorNaam = prVoorNaam End Get Set(ByVal value As String) prVoorNaam = value End Set End Property Public Property GeboorteDatum() As String Get GeboorteDatum = prGeboorteDatum End Get Set(ByVal value As String) prGeboorteDatum = value End Set End Property Public Property Gemeente() As String Get Gemeente = prGemeente End Get Set(ByVal value As String) prGemeente = value End Set End Property Public Function combineer() As String Return prVoorNaam + " " + prNaam End Function End Class
11
rd Merk op dat we een functie combineer plaatsen, dit om het juiste formaat in onze listbox te krijgen. Nadien ontwerpen we ons formulier zoals op bovenstaande tekening. In het formulier plaatsen we enkel globale variabelen Dim geg() As gegevens Dim Toevoegen As Boolean Dim aantal As Integer
Merk op dat we de tabel geg() maken zonder aanduiding van het aantal elementen. Immers we weten niet hoeveel elmenten we gaan in deze oefening gebruiken. Dit wordt dan zoals we noemen een dynamische tabel waarvan de grote afhangt van aantal die we als integer declareren. De Booleaanse viarabele Toevoegen zal gebruikt worden telkens we op bewaren gaan drukken. Hierna staan al de gebruikte procedure en functies. Private Sub bntVoegToe_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bntVoegToe.Click grbGegevens.Enabled = False grbDetail.Enabled = True btnSave.Visible = True btnCancel.Visible = True Toevoegen = True Call maakleeg() txtNaam.Focus() End Sub Private Sub maakleeg() txtNaam.Clear() txtVoorNaam.Clear() txtGebDat.Clear() txtGemeente.Clear() End Sub Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click Call maakleeg() btnSave.Visible = False btnCancel.Visible = False grbDetail.Enabled = False grbGegevens.Enabled = True End Sub Private Sub vulListbox() Dim T As Integer lstGegevens.Items.Clear() For T = 1 To aantal lstGegevens.Items.Add(geg(T - 1).combineer) Next If lstGegevens.Items.Count > 0 Then lstGegevens.SelectedItem = 0 End If End Sub
12
rd Private Sub selekteer() Dim Indeks As Integer If lstGegevens.SelectedIndex >= 0 Then Indeks = zoekIndex(lstGegevens.SelectedItem) txtNaam.Text = geg(Indeks).Naam txtVoorNaam.Text = geg(Indeks).VoorNaam txtGebDat.Text = geg(Indeks).GeboorteDatum txtGemeente.Text = geg(Indeks).Gemeente End If End Sub Private Function ZoekIndex(ByVal waarde As String) As Integer Dim T As Integer For T = 0 To aantal - 1 If geg(T).combineer = waarde Then Return T End If Next End Function
Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click 'Dim ZoekW As String Dim Indeks As Integer If Toevoegen = True Then aantal += 1 Indeks = aantal - 1 ReDim Preserve geg(Indeks) geg(Indeks) = New gegevens geg(Indeks).Naam = txtNaam.Text geg(Indeks).VoorNaam = txtVoorNaam.Text geg(Indeks).GeboorteDatum = txtGebDat.Text geg(Indeks).Gemeente = txtGemeente.Text Else Indeks = ZoekIndex(lstGegevens.SelectedItem) geg(Indeks).Naam = txtNaam.Text geg(Indeks).VoorNaam = txtVoorNaam.Text geg(Indeks).GeboorteDatum = txtGebDat.Text geg(Indeks).Gemeente = txtGemeente.Text End If Call VulListbox() Call btnCancel_Click(sender, e) Call selekteer() End Sub Private Sub lstGegevens_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstGegevens.SelectedIndexChanged Call selekteer() End Sub Private Sub btnBewerk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBewerk.Click If (lstGegevens.SelectedIndex >= 0) Then grbDetail.Enabled = True
13
rd grbGegevens.Enabled = False Toevoegen = False btnSave.Visible = True btnCancel.Visible = True txtNaam.Focus() End If End Sub
Private Sub btnVerwijder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnVerwijder.Click Dim Indeks As Integer Dim T As Integer If lstGegevens.SelectedIndex >= 0 Then If MsgBox("mag dit gegeven verwijderd worden?", _ MsgBoxStyle.YesNo + MsgBoxStyle.Question + _ MsgBoxStyle.DefaultButton2, "gegeven wissen") = MsgBoxResult.Yes Then Indeks = ZoekIndex(lstGegevens.SelectedItem) For T = Indeks To aantal - 2 geg(T) = geg(T + 1) Next aantal -= 1 ReDim Preserve geg(aantal) Call vulListbox() End If End If End Sub
Het moeilijkste van dit programma is de code bij de knop “bewaar”. Dit vraagt om enige verduidelijking: Als de gebruiker op het bewaar drukt dan zal hij verschillende zaken beogen. Eerst wordt er nagegaan als het om een toevoeging gaat dan wel over een verandering. Daarvoor gebruiken we de booleaanse variabele. Bij een wijziging moet het bestaande veld overschreven worden. Om vast te stellen dat het om een toevoeging gaat zullen we de variabele op true zetten. Als we edit programmeren zetten we deze variabele eerst op false. Als we toevoegen dan zal aantal met 1 verhgogen, de tabel zal opnieuw gedimensioneert worden, we initialiseren een nieuw element en voegen de velden toe. Gaan we echter een veld bewerken, dan zoeken we eerst op om het hoeveelste element in de tabel het gaat en we zorgen dat deze overschreven wordt. Een nadenkertje. De elementen staan niet altijd zoals opgeven in onze listbox. Het is mogelijk dat in onze liestbos de elementen gesorteerd zijn, dat zal niet zo zijn bij het inlezen van onze tabel! Hierbij is ons programma gedaan. Natuurlijk als u de ingebrachte gegevns wil bewaren moeten we er nog iets bij maken. Het bewaren in tabellen dat is voor het ogenblik nabije toekomst muziek! Nu gaan we ze opslaan in, jawel, een bestand. Daarvoor gaan we eerst iets 14
rd in een bestand leren plaatsen. Na het volgende hoofdstuk komen we terug op deze oefening om de gegevens in een bestand te plaatsen en zo onze oefening te vervoledigen.
15
rd
Hoofdstuk 15 Dialoogvensters Het is natuurlijk niet allemaal kommer en kwel met VB.net. Er bestaan ook leuke zaken in deze omgeving. Daar gaan we nu een en paar van zien. Natuurlijk zijn die niet allemaal zo eenvoudig te begrijpen, maar wel soepel te hanteren. Veelal is dat je van uit je programma’s hulp kunt gebruiken van de OS. Deze hulp wordt u aangeboden langs dialoogvensters. Als u bij gebruik van uw programma aan de gebruiker de mogelijkheid wilt geven fonts aan te passen of eventueel kleurtjes aan te passen kunt u gebruik maken van bestaande mogelijkheden van uw besturingssoftware. Visual studio geeft u de mogelijkheid om te werken met onderstaande dialoogvensters -
Color Font Open Save Print
In sommige oefeningen vroeger hebben we al gewerkt met deze dialoogvensters, ik denk bij voorbeeld aan de oefening met de cirkels. Dat was een leuk iets en we kunnen dit even nader bekijken.
15.1 Dialoogvenster Color
Fig. 8
16
rd We maken een formulier zoals in figuur 80. Eerst plaatsen we daar een knop op btnKleur (“kleur”) en een textveld txtKleur met multiLine = true. In onze toolbox zien we het onderdeel staan dialoog.(fig81) dat openen we en zien we daarin verschillende dialogen staan.
Fig. 9
We nemen color dialog en plaatsen dat op onze form. Onmiddellijk zal dat naar beneden schuiven. We geven deze dialoog(properties) de naam cdlKleur. De bedoeling is nu als de gebruiker op de knop klikt dat hij ken kiezen tussen de kleuren die aangeboden worden door het OS. Daarvoor moet u in de knop het volgende programmeren: Private Sub btnKleur_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnKleur.Click cldKleur.Color = txtDialoog.BackColor cldKleur.AllowFullOpen = True If cldKleur.ShowDialog = Windows.Forms.DialogResult.OK Then txtDialoog.BackColor = cldKleur.Color End If End Sub
Fig. 10
17
rd Bovenstaande figuur is het resultaat en als je op een kleur drukt zal je zien dat het tekstvak veranderd van kleur.
15.2 Dialoogvenster font
Fig. 11
We breiden onze figuur uit met een knop Font. Nadien gaan we ook het dialoogvenster Font inbrengen en noemen het cldfont de knop programmeren we als volgt: Private Sub btnFont_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFont.Click cldFont.Font = txtDialoog.Font cldFont.ShowColor = True cldFont.Color = txtDialoog.ForeColor cldFont.ShowApply = True If cldFont.ShowDialog = Windows.Forms.DialogResult.OK Then txtDialoog.Font = cldFont.Font End If End Sub
En we testen
15.3 Dialoogvenster OpenFileDialoog Met deze kan je toegang krijgen tot mappen en bestanden die op je schijf staan. Met deze kunnen we tekstbestanden, bat bestanden, ini bestanden… openen en afbeelden in en tekstvenster.. Om deze te gebruiken moeten we nog een speciaal object aanspreken namelijk het StreamReader object. Later zal dat nog ter sprake komen maar hier volstaat het dat u de code kent om een bestand te openen en te brengen naar een tekstbox. Die code is:
18
rd Dim SR as System.IO.StreamReader SR = System.IO.File.OpenText(“c:\Windows\win.ini) txtTekst.Text = SR.ReadToEnd()
Fig. 12
Op ons bestaand formulier gaan we een knop Open (btnOpen) plaatsen en de dialoogbox OpenFileDialog toevoegen. We geven deze als naam cldOpen Anders dan bij de ander dialogen gaan we nu even stilstaan bij enkel eigenschappen van deze dialoog, welke kunnen belangrijk zijn bij het bewaren van bestanden. 15.3.1 AddExtension Deze is enkel van belang als we een bestand willen bewaren. Deze Boole- waarde bepaald enkel als het dialoogvenster automatisch een extensie aan het bewaarde bestand zal toekennen. Dit wordt bepaald door de DefaultExtension, die moet worden ingesteld voor men de showdialoog aanroept. 15.3.2 CheckFileExist Anders dan in de taal C zal men hier niet expliciet moeten checken als het bestand bestaat. We kunnen terug een vlag instellen die aangeeft als het bestand al dan niet bestaat in het pad dat de programmeur aangeeft. 15.3.3 FileName Deze eigenschap is het toegangspad van het bestand dat de gebruiker in het besturingselement heeft geselecteerd. 19
rd 15.3.4 Filter Met deze eigenschap zal men het type van bestanden die men wil zien, aanduiden. Het pijp symbool (|) zal de beschrijving van het symbool, dat wat de gebruiker ziet, scheiden van dat wat de computer zal onderscheiden. Verder komen we daar nog op terug. 15.3.5 FileNames Als de dialoog meerdere bestanden toelaat zal deze eigenschap de padnamen van de bestanden bevatten Er zijn nog verschillende andere eigenschappen maar deze hebben we nu niet nodig. Bij gebruik ervan zullen ze wel toegelicht worden. 15.3.6 de code bij de knop open: Private Sub btnOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpen.Click Dim myStreamReader As System.IO.StreamReader Dim BestandsNaam As String cldOpen.InitialDirectory = Application.ExecutablePath If cldOpen.ShowDialog = DialogResult.OK Then BestandsNaam = cldOpen.FileName myStreamReader = System.IO.File.OpenText(BestandsNaam) txtDialoog.Text = myStreamReader.ReadToEnd myStreamReader.Close() myStreamReader.Dispose() End If End Sub
In deze code staan er nog een aantal speciale zaken. Één ervan is muStreamreader.close Anders dan bij vele talen moet je ook hier na lezen het bestand sluiten en met myStreamReader.dispose zal je het geheugen opkuisen. Belangrijk voor de snelheid van uw computer.
Fig. 13
20
rd Figuur 85 geeft een bestand die ik gelezen heb van op mijn computer
15.4 Dialoogvenster Save Deze werkt bijna identiek als de vorige. Met deze kunnen we het tekstvak dat we hebben opslaan op de computer. We plaatsen een knop bij op ons formulier en gaan het dialoogvenster gaan halen in onze toolbox. We geven het de naam cldSave. We gaan ons echter beperken tot het opslaan van de inhoud van een tekstvak op te slaan als tekstvak. Daarvoor gaan we terug een andere dialoog controle opslaan namelijk de SaveFileDialog. Even als de andere halen we deze ook vanuit onze toolbox en plaatsen deze op het formulier, en geven die de naam cldSave.
Fig. 14
We gaan terug een opslaan knop (btnSave) in ons formulier introduceren en daaraan hangen we onderstaande code:
Fig. 15 Dim Bestnaam As String Dim SW As System.IO.StreamWriter If cldSave.ShowDialog = DialogResult.OK Then Bestnaam = cldSave.FileName If System.IO.File.Exists(Bestnaam) Then If MsgBox("Bestand bestaat reeds! Overschrijven ?", _ MsgBoxStyle.YesNo, "save") <> MsgBoxResult.Yes Then Exit Sub End If End If SW = System.IO.File.CreateText(Bestnaam) SW.Write(txttest.text) SW.Flush()
21
rd 'plaats de inhoud van de gebruikte buffer en schrijft het naar de stream SW.Close() End If We zien wel dat we geen keuze hebben in het opslaan van ons bestand. Om dat te bekomen moeten we iets veranderen in de eigenschappen van ons dialoog venster.
Bij het gedeelte filter van de eigenschappen van ons dialoog gaan we invullen welke extensie we willen. In ons geval tekstbestand en daarbij een rechte separator en *.txt. Daardoor wordt het opgeslagen als txt bestand. Nog iets waar men moet opletten is dat we OverWritePrompt steeds op False plaatsen. Immers, anders heeft onze if structuur in onze code geen zin. Gebruiken we per ongeluk een zelfde naam zal het op te slaan bestand het andere automatisch overschrijven.
15.5 het dialoogvenster printer Vroegere programmeurs in VB zullen beamen dat het printen in deze taal een hekelpunt is. In de versie VB.Net is er al een en ander ten goede veranderd, maar ondanks dat blijft het toch steeds een behelpen. Microsoft zelf stelt voor om als men veelvuldige rapportage en afdruk van gegevens moet doen gebruik te maken van software van derden. Verder op zullen we uitgebreid ingaan op het afdrukken van gegevens. Hier zullen we ons beperken tot het afdrukken van de inhoud van een tekstvakje. Breng op het formulier nog een knop (btnAfdrukken) en plaats vervolgens het afdrukdialoog printdialog en geef het de naam cldPrint. Plaats tevens de dialoogcomponent PrintDocument op de form en geef het de naam PrintDoc. Fig.16
.
22
rd Let op: het enige dat het besturingselement Printdialog doet is het tonen van de beschikbare printers (+ een paar instellingen) op het scherm. Ondanks de naam zal dit element NIET instaan voor de afdruk. Deze moeten we programmeren. Daarvoor bestaat er een nieuw element in VB.Net namelijk PrintDocument. Deze zal zorgen dat u alle printopdrachten kunt afwerken. Let wel zonder dit object kunt u niet afprinten Voeg onderstaande code toe aan de knop om te printen alsook aan het printdocument dialoog. Om daar een code aan toe te voegen dient u enkel te dubbelklikken op de PrintDoc element Private Sub btnAfdruk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfdruk.Click cldPrint.AllowPrintToFile = False cldPrint.AllowSelection = False cldPrint.PrinterSettings = _ New System.Drawing.Printing.PrinterSettings If cldPrint.ShowDialog = DialogResult.OK Then PrintDoc.PrinterSettings.PrinterName = _ cldPrint.PrinterSettings.PrinterName() PrintDoc.Print() End If End Sub Private Sub PrintDoc_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDoc.PrintPage Dim printFont As New Font("Verdana", 10, GraphicsUnit.Point) e.Graphics.DrawString(txtTest.Text, printFont, Brushes.Black, 20, 20) e.HasMorePages = False End Sub End Class
23
rd
Hoofdstuk 16 Klassen (deel3) In vorige beschouwingen hebben we steeds beweerd dat een klasse een op zichzelf staand programma is. Dit houdt dan in als je een methode uit een klasse lanceert deze code uitgevoerd wordt als een apart proces naast het programma dat u al aan het uitvoeren bent. Zo een proces dat op zichzelf draait noemt men een thread. Dus als u een programma aan het draait en u start een methode uit een klasse vanuit dat programma, dan zullen er 2 processen of 2 threads (draden) draaien. Voorheen hebben we daar nog niet bij stilgestaan, echter dat gebeurde ook. Omdat ons programma moest wachten tot de thread welke door de klasse was begonnen beëindigd was, hebben we hier ook niets voor gedaan. Stel dat de thread dat u opstart vanuit uw programma een lange verwerkingstijd nodig heeft en u de gebruiker gedurende die tijd de kans wil geven iets te doen met zijn programma, hoe kan u dit oplossen? In dit geval moet u voorzieningen treffen dat een bepaalde thread niet al de processor tijd opneemt. Men zegt in dit geval dat we aan moeten Multi-threading moet doen. Er wordt wel beweerd dat Windows een Multi-threading systeem is, maar u zult onmiddellijk bemerken dat dit in de praktijk niet zo evident is. Neem nu dat we een programma willen ontwerpen die met behulp van de recursieve methode de volgende zaken moet oplossen. - Opzoeken van een gegeven map het aantal submappen - Opzoeken van het aantal bestanden in deze - Opzoeken van het aantal bytes in die bestanden Volgens het aantal informatie dat op u te onderzoeken schijf staat kan dit eventueel vele seconden en zelfs minuten voor nodig heeft Dit programma zal zeker proberen dit zo vlug mogelijk tot een goed eind te brengen en zal u de indruk kunnen geven dat het programma de computer doet “vast lopen” dit zal zo blijven tot de methode afgelopen is. Maar omdat Windows nu een “Multi-threading operating system” is, is het toch mogelijk om de beschikbare processor tijd te verdelen over de verschillende threads die uw programma start. Het is nu aan u de programmeur er voor te zorgen dat de threads de processor tijd verdelen onder elkaar. Je kunt immers stellen dat de threads een egoïstische karaktertrek 24
rd hebben en alles zoveel veel mogelijk voor hen willen. Om dit op te lossen hebben we een instructie die we in onze methode kunnen steken namelijk: Application.DoEvent() Deze instructie organiseert een software interrupt die op zijn beurt ervoor zorgt dat andere programma’s of andere threads in het zelfde programma ook een tijdje de kans krijgen hun eigen instructies uit te voeren. We spreken hier van context-switch. Een klein voorbeeld We gaan een project maken die een klasse bevat die van een gegeven map de gegevens gaat onderzoeken. We ontwerpen een form zoals in de figuur
Fig. 17 Dit bestaat uit de tekstvakken txtmap1..3, btnmap1..3, btnMap1..3Zoek en lblResult1..3. Bij die form voegen we de dialoog FolderBrowser en noemen die FBrowser. Die zal zorgen dat we een map kunnen selecteren in uw systeem of zelfs in een netwerk. De belangrijkste eigenschap van deze is SelectedPath. Met deze kan men een bepaalde map sturen en eventueel uitlezen. Als men klikt op de knop btnMap1..3Zoek zal Fbrowser een dialoogvenster openen waarin een bepaalde map kan geselecteerd worden. Deze dient dan te verschijnen in de aanvullende 25
rd tekstvakken, txtmap1.3. Let op om dat voor alle 3 de knoppen hetzelfde is zal men dat schrijven in 1 Event.
Private Sub btnMapzoek_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnMap1zoek.Click, _ btnMap2Zoek.Click, _ btnMap3Zoek.Click Dim TekstVak As TextBox If sender Is btnMap1zoek Then TekstVak = txtmap1 End If If sender Is btnMap2Zoek Then TekstVak = txtmap2 End If If sender Is btnMap3Zoek Then TekstVak = txtmap3 End If If TekstVak.Text <> "" Then Fbrowser.SelectedPath = TekstVak.Text End If If Fbrowser.ShowDialog = Windows.Forms.DialogResult.OK Then TekstVak.Text = Fbrowser.SelectedPath End If End Sub Fig. 18
Om echter te weten welke van de drie knoppen ons daartoe zal brengen en de juiste geselecteerde mappen in de juiste tekstvakken te plaatsen onderzoeken we de parameter sender. Let op: om sender (een object) te vergelijken met een knop mag men niet de booleaans variabele ‘=’ gebruiken als symbool maar het sleutelwoord Is In de volgende code die staat bij de knoppen btnmap1..3 hebben we het zelfde fenomeen Private Sub btnMap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnMap1.Click, _ btnMap2.Click, _ btnMap3.Click Dim TekstVak As TextBox Dim labelVak As Label Dim D As New DiscScanner If sender Is btnMap1 Then TekstVak = txtmap1 labelVak = lblResult1 End If If sender Is btnMap2 Then TekstVak = txtmap2
26
rd labelVak = lblResult2 End If If sender Is btnMap3 Then TekstVak = txtmap3 labelVak = lblResult3 End If labelVak.Text = D.ScanDir(TekstVak.Text) D = Nothing End Sub Fig. 19
Merk op dat we in deze code een klasse ScanDir oproepen. Deze zullen we ook moeten construeren. We werken met 4 private variabelen: Public Class DiscScanner Private pr_AantalBestanden As Long Private pr_AantalDirectories As Long Private pr_AantalBytes As Long Private pr_Mapnaam As String fig. 20
Hoewel het niet speciaal nodig is (en we gaan er ook hier geen gebruik van maken) leek het me toch in het licht van andere volgende oefeningen, de variabelen ReadOnly te maken. ReadOnly Property AantalBestanden() As Long Get AantalBestanden = pr_AantalBestanden End Get End Property ReadOnly Property AantalDirectories() As Long Get AantalDirectories = pr_AantalDirectories End Get End Property ReadOnly Property MapNaam() As String Get MapNaam = pr_Mapnaam End Get End Property fig. 21
Als volgende hebben we de methode ScanDir. Deze methode verwacht een parameter, de te onderzoeken map, en zal een rapportje terug geven. Zo niet zal het de mededeling geven dat we een ongeldige map aan het bekijken zijn. Het is in feite de bedoeling deze controle recursief te laten gebeuren. Daarom zullen we het zoekwerk niet in deze methode laten gebeuren. Het is immers denkbeeldig dat vanuit de methode SacnDir zich zelf oproept. Daarom zetten we van in het begin de tellers op 0 zodat bij herhaalde oproep de tellers niet meer ge updatet worden. Dit geeft echter als negatief 27
rd punt dat we deze methode niet meer kunnen recursief benaderen (waarom?)…Inderdaad dar de tellers steeds op 0 geplaatst worden zouden de resultaten niet meer kloppen Public Function ScanDir(ByVal InitialDirectory As String) As String Dim Rapport As String pr_AantalDirectories = 0 pr_AantalBestanden = 0 pr_AantalBytes = 0 pr_Mapnaam = InitialDirectory If IO.Directory.Exists(pr_Mapnaam) Then OnderzoekMap(pr_Mapnaam) Rapport = "resultaat" & pr_Mapnaam & vbCrLf Rapport = Rapport + "aantal mappen: " & pr_AantalDirectories.ToString + vbCrLf Rapport = Rapport + "aantal bestanden: " & pr_AantalBestanden.ToString + vbCrLf Rapport = Rapport + "aantal bytes: " & pr_AantalBytes.ToString Return Rapport Else Return ("ongeldige map op gegeven") End If End Function fig. 22
Daarom gaan we de berekening laten gebeuren in een andere private functie. Deze functie noemt OnderzoekMap() Private Function OnderzoekMap(ByVal map As String) Dim Bestandsnaam As String Dim Mapnaam As String Dim fi As IO.FileInfo For Each Bestandsnaam In IO.Directory.GetFiles(map) pr_AantalBestanden += 1 fi = New IO.FileInfo(Bestandsnaam) pr_AantalBytes = pr_AantalBytes + fi.Length Application.DoEvents() Next For Each Mapnaam In IO.Directory.GetDirectories(map) pr_AantalDirectories += 1 OnderzoekMap(Mapnaam) Application.DoEvents() Next End Function fig. 23
Als u het programma uitvoert zal men kunnen 3 maal ‘tegelijkertijd’ een map onderzoeken. Door het gebruik van de methode ApplicationDoEvents() kunnen we dat doen. Als u deze zou weg doen dan kan men zelfs geen tweede zoekactie starten! Probeer dit maar uit. 28
rd
2. Instanties Als men vanuit een programma een klasse willen gebruiken zullen we dat organiseren als volgt: Dim d as New MijnKlasse. In dit geval zeggen we dat de een instantie is van de klasse. In feite is een instantie niet meer dan een kopie van mijn klasse die we dan kunnen gebruiken. Herinneren we ons de twee plaatsen in het geheugen waar we onze informatie opslaan. Namelijk de stack en de heap. De stack zorgt er voor dat we iedere variabele die we declareren en plaats geven. Deze plaats wordt de grootte van wat we aanvragen (bvb Integer of string, of long…enz).zodra die variabele afsterft, zal de plaats terug (automatisch) vrijgegeven worden. Als men echter een objectvariabele aanmaakt (instantie creëert van een klasse) wordt die kopie op de heap geplaatst en niet op de stack. Bvb dim c as new Cirkel zorgt er voor dat een kopie van de klasse cirkel op de heap geplaatst wordt. Let wel ogenblikkelijk wordt er voor iedere gedeclareerde variabel uit die klasse een ruimte gereserveerd op de stack! Stel dat je twee copies van die klasse dient aan te maken. Logisch zal je denken dat er direct 2 copies op het heap zal geplaatst worden met de nodige plaatsen op de stack. Toch niet! Immers er staat al een code van die klasse op de heap. Op de stack echter worden er wel een tweede set variabelen aangemaakt en geïnitialiseerd met 0. Stel nu de methode cirkel enkel uit 1 property (variabele) bestaat Public Class cirkel Public Straal as Double End Class Declareren we: Dim c1 as New Cirkel
wordt de code op de Heap geplaatst. Op het zelfde moment wordt er een plaats op de stack gereserveerd voor de variabele straal en geïnitialiseerd met 0. Als we nu het volgende declareren: Dim c2 as new Cirkel wordt de code niet meer op de heap geplaatst (immers de code bestaat al). Maar wel wordt er een nieuwe variabele Straal op de stack gemakt en geïnitialiseerd met 0. Breiden we onze klasse uit met een methode die de diameter berekend (straal * 2) 29
rd Public Class cirkel Public Straal As Double Public Function Diameter() As Double Dim getal As Integer Getal = 2 Return Getal * Straal End Function End Class Onze programma doet nu het volgende: Dim c1 As New Cirkel (1) Dim c2 As New Cirkel (2) c1.Staal = 5 (3) c2.Straal = 8 (4) MsgBox(c1.Diameter)) (5) msgbox(c2.Diameter)) (6) (1) Er word teen copie van cirkel op de heap geplaatst. Onmiddellijk wordt er op de stack een plaats voor de variabele c1 (double) gemaakt en geïnitialiseerd met 0. (2) Hier wordt geen nieuwe copie op de heap geplaatst meer wel wordt c2 aangemaakt. (3) De variabele c1 wordt gevuld met 5 (4) De variabele c2 wordt gevuld met 8 (5) De methode diameter vanuit de eerste copie wordt uitgevoerd en dit met c1 die op de stack geplaatst was. De berekening wordt uitgevoerd en het resultaat getoond. De methode is teneinde en het variabele getal, gereserveerd ten behoeve van c1 wordt vrijgegeven. (6) De methode diameter vanuit de tweede copie wordt uitgevoerd en dit met c2 die op de stack geplaatst was. De berekening wordt uitgevoerd en het resultaat getoond. De methode is teneinde en het variabele getal, gereserveerd ten behoeve van c2 wordt vrijgegeven. Wat gebeurd er nu met de heap en de variabelen(c1 en c2)die op het stack staan? Wel die plaatsen op de stack worden vrij gegeven bij het einde van hun bereik. Het object op de heap blijft echter wel bestaan. Immers het kan zijn dat je die cirkel verder in het programma dient te gebruiken. Echter geen nood de .net makers hebben daar rekening mee gehouden. Dart door het maken van GC of garbage collector (vuilbak). De GC gaat regelmatig het heap onderzoeken en kijken als de objecten welke er op staan nog gebuikt worden (zijn de 30
rd instanties nog actief?) en moet hij daar nog blijven. Zo niet wordt hij automatisch weggenomen. Men kan wel de GC een beetje helpen indien nodig. Dit door te schrijven: c1 = NOTHING c2= NOTHING Dit wil niet zeggen dat de code van het heap moet verdwijnen maar dat de gegeven variabele niet meer nodig is, zodat de GC zelf dat niet meer moet nakijken. Als dit laatste fenomeen zich voordoet, dan wordt, natuurlijk indien aanwezig, de code van de finalize routine uitgevoerd. Protected Overrides Sub Finalize() Messagebox.Show(“Slaapwel van “ + pr_Naam End Sub.
3. gedeelte eigenschappen
De aanwezigheid van gedeelde eigenschappen is het beste bewijs dat de klasse maar 1 maal op de heap geplaatst wordt. Deze eigenschappen( variabelen) worden slechts 1 maal aangemaakt. Dat wil zeggenvoor deze eigenschappen wordt er maar ruimte voorzien voor 1 set op de stack. We gaan dit declareren met het sleutel woord Shared. Voorbeeld: Public Class DiscScanner Private pr_AantalBestanden As Long Private pr_AantalDirectories As Long Private pr_AantalBytes As Long Private pr_Mapnaam As String Public Shared AantalGebruikers As Integer fig. 24
Telkens we de methode scandir wordt uitgevoerd gaan we deze variabele verhogen met 1 Public Function ScanDir(ByVal InitialDirectory As String) As String Dim Rapport As String pr_AantalDirectories = 0 pr_AantalBestanden = 0 pr_AantalBytes = 0 pr_Mapnaam = InitialDirectory AantalGebruikers += 1 If IO.Directory.Exists(pr_Mapnaam) Then fig. 25
Men zorgt voor een knop bij het form gedeelte en voegt er de code in figuur 27 toe 31
rd
Fig. 26 Private Sub btnAantalGebruikers_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAantalGebruikers.Click Dim vraag As New DiscScanner MessageBox.Show(vraag.AantalGebruikers) vraag = Nothing End Sub fig. 27
Als laatste voer uw programma uit en bekijk het resultaat.
4. Encapsulation en Overloading Tot op dit ogenblik zijn onze klasse nog altijd rechtlijnig geprogrammeerd. In feite wijkt dat eigenlijk niet af van het gewone programmeren in VB6. Dan rijst de vraag waarom klassen maken, zonder zou het merkelijk gemakkelijker zijn, is het niet? Een belangrijk voordeel hebben we al besproken, namelijk de herbruikbaarheid van deze klassen. Eenmaal een klasse gemaakt kunnen we met een paar ingrepen een DLL-bestand (Dynamically Linked Lirary). Het is ook natuurlijk mogelijk om code te copieren en plakken in de verschillende programma’s, maar wat gebeurt er dan als er een fout zit in een van de methoden van de klasse, of dat re wijzigingen moeten aangebracht worden? Als u niet met klassen werkt moet men gans het programma onderzoeken waar de code overal staat om ze te veranderen. Als men een klasse heeft geschreven dan volstaat het eenmaal de klasse aan te passen en het euvel is opgelost. Een tweede voordeel van de klassen is Encapsulation of inkapseling Dit wil zeggen dat de ontwikkelaar van de klasse zelf beslist wat de wereld buiten het programma kan zien. De gebruiker zal dan enkel moeten weten wat hij naar de klasse stuurt en wat hij zal terug krijgen. Het is dan wel de verantwoordelijkheid van de ontwikkelaar van e klasse dat, eenmaal de klassen gebruikt in andere projecten of door andere gebruikers men niet meer afwijkt van de functionaliteit van de klasse. D.w.z dat als een methode van de klasse bijvoorbeeld een 32
rd waarde teruggeeft van het type integer u morgen niet mag beslissen dat het een gegeven is van het type string. Wel kan men in extremis de klasse uitbreiden, zolang u er maar voor waakt dat de oorspronkelijke methode blijft functioneren. Stel dat je een klasse bereken hebt gemaakt met de functie dat een getal verdubbelt: Public Function vedubbel(ByVal waarde As Double) As Double Return waarde = waarde * 2 End Function fig. 28
Nadat de functie een tijdje in gebruik is vind je het wenselijk aan deze een tweede functie toe te voegen waar een omzetting van type in verwerkt is. Public Function vedubbel(ByVal waarde As Double) As Double Return waarde = waarde * 2 End Function Public Function verdubbel(ByVal str_waarde As String) As Double If IsNumeric(str_waared) Then Return Double.Parse(str_waarde) * 2 Else Return 0 End If End Function fig. 29
Bij een nieuwe implementatie van de klasse kan men kiezen tussen de twee manieren.
5. overerving Uit de stelling hierboven gaan we uit van het feit dat voor iedere uitbreiding van de klasse de programmeur, hoe klein die ook mag zijn, er een uitbreiding moet worden geschreven. Dat zou een Beetje te ingewikkeld zijn. Een eigenschap die ook te gebruiken is, maakt dat bij het schrijven van een klasse men de functionaliteit van een bestaande klasse kan overnemen, zonder dat de andere klasse in functionaliteit inboet. Om dit principe te benaderen zullen we een voorbeeld uitwerken. Daarin moeten we één ding voor het moment aannemen dat een ArrayList een dynamische Array is dat om het even welk object kan bevatten. Een ArrayList beschikt een heleboel methoden, maar het beschikt niet over een methode die dubbele waarde verwijdert uit de lijst. Dit gaan we er bij maken. Maak een project aan en voeg er een klasse aan toe: MyArrayList. Koppel nu Arraylist aan uw klasse met de term Inherits. Bij de functie Inherits of overerving zal uw klasse al de methodes van die andere klasse zich eigen maken en klaar maken om te gebruiken. Laat ons eens dat voorbeeld bekijken. 33
rd
Fig. 30
We maken een form met 2listboxen en een knop (zie figuur). We maken aan dit project zoals voordien gezegd een klasse met erving van functies door de Klasse ArrayList op volgende wijze: Public Class myArrayList Inherits ArrayList Public Sub VerwijderDuplicaten() Dim Teller1 As Integer = 0 Dim teller2 As Integer = 0 Dim aantal As Integer = 0 aantal = MyBase.Count - 1 Dim Waarde As String Do While Teller1 < aantal Waarde = MyBase.Item(Teller1) teller2 = Teller1 + 1 Do While teller2 <= aantal If MyBase.Item(teller2).ToString = Waarde Then MyBase.RemoveAt(teller2) aantal -= 1 teller2 -= 1 End If
34
rd teller2 += 1 Loop Teller1 += 1 Loop End Sub End Classfig. 31
Merk op dat op het moment dat we ArrayList er aan toevoegen we al in onze form kunnen gebruik maken van al de eigenschappen van de klasse ArrayList. Probeer maar eens ja zal het wel zien. Onze form geven we de onderstaande code: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load lstA.Items.Add("A") lstA.Items.Add("B") lstA.Items.Add("C") lstA.Items.Add("a") lstA.Items.Add("c") lstA.Items.Add("A") lstA.Items.Add("C") lstA.Items.Add("B") lstA.Items.Add("Z") lstA.Items.Add("c") lstA.Items.Add("a") lstA.Items.Add("8") lstA.Items.Add("5") lstA.Items.Add(5) lstA.Items.Add(8) lstA.Items.Add(6) lstA.Items.Add(8) lstA.Items.Add(5) End Subfig. 32
Door deze code zullen we zien dat er een bepaald aantal gegevens in onze listbox komen. Door nu de knop te programmeren zien we dat sommige zullen overgezet worden naar de volgende listbox, de dubbele gegevens zullen niet overgeplaatst worden Private Sub btnSchuif_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSchuif.Click lstB.Items.Clear() Dim mA As New myArrayList mA.AddRange(lstA.Items) mA.VerwijderDuplicaten() lstB.Items.AddRange(mA.toArray) mA = Nothing End Subfig. 33
Uiteindelijk komen we het volgende uit: 35
rd
Fig. 34
36
rd
Hoofdstuk 17 Werken met meerdere formulieren (uitbreiding) In vorig hoofdstuk 12 hebben we al gewerkt gebruikmakend van meerdere formulieren. Onze programma’s waren opgebouwd uit het SDI1 principe. Dit werd bekeken als een afzonderlijk proces. U kunt dat best bekijken dat u een ander formulier opent zal u een ander icoontje maken in Windows. 17.1 Show en ShowDialog Vooraleer we een nieuw formulier maken zullen we eerst moeten objectvariabele maken om naar ons nieuw formulier te verwijzen. Natuurlijk want ieder formulier zal fungeren als klasse. Stel we maken een project bestaand uit 2 formulieren. In het eerste formulier plaatsen we een knop en in zijn handler plaatsen we: Dim f as new form2 Wilt u nu uw formulier starten dan zal je moeten inbrengen: f.Show() of f.ShowDialog. Het verschil tussen beide commando’s: Bij ShowDialog kan men form1 niet meer gebruiken zolang form2 open staat, wat men wel kan als men show gebruikt. Daar we werken met een verwijzing naar een object is het mogelijk verschillende malen dit object te openen (zie vroeger). Dit is wel niet gebruikelijk maar het kan voorkomen dat men het moet doen. 17.2 Voorbeeld project Maak een nieuw project aan volgens onderstaande figuur
1
SDI = Single Document Interface
37
rd
Fig. 35
Maak het menu structuur als volgt: MenuItem
naam
Bestand
mnuBestand Openen
mnuOpen
Opslaan
mnuOpslaan
-
nvt
Afsluiten
mnuAfsluiten
38
rd Bewerken
mnuBewerken
Kopiëren
mnuKopiëren
Knippen
mnuKnippen
Plakken
mnuPlakken
Nieuw Formulier
mnuNieuwFormulier
Het is de bedoeling dat we met dit programma tekstbestanden kunnen opvragen of teksten in een bestand kunnen opslaan. We doen dit met behulp van het Streamwriter en het StreamReader Object. Het uiteindelijk lezen van- en bewaren van de gegevens in een bestand gaan we natuurlijk regelen in een klasse. Maak daarom in uw project een klasse clsTxtReadWrite aan met onderstaande inhoud: Public Class clsTxtReadWrite 'klasse voor lezen en schrijven van een bestand Public Function LeesBestand(ByVal bestandsnaam As String) As String Dim SR As System.IO.StreamReader 'vaiabele om te lezen (StreamRead) Dim Retour As String If System.IO.File.Exists(bestandsnaam) Then 'controleren als het bestand bestaat Sr = System.IO.File.OpenText(bestandsnaam) Retour = SR.ReadToEnd 'lezen bestand en terugsturen Sr.Close() Else Retour = "" 'bij geen bestand ledige string terug sturen End If SR = Nothing 'verwijderen van SR uit het geheugen Return Retour End Function Public Sub SchrijfBestand(ByVal bestandsnaam As String, ByVal inhoud As String) Dim SW As System.IO.StreamWriter SW = System.IO.File.CreateText(bestandsnaam) 'creëren van bestand op schijf SW.Write(inhoud) 'inschrijven van de inhoud in het geheugen SW.Flush() 'inhoud van het geheugen naar de schijf voeren SW.Close() 'sluiten van het bestand SW = Nothing End Sub End Class
Nadien gaan we de verschillende menu items programmeren. Code van mnuOpen: 39
rd
Private Sub mnuOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuOpen.Click If cdlOpen.ShowDialog = Windows.Forms.DialogResult.OK Then Dim LB As New clsTxtReadWrite 'instantie maken van de klasse txtInhoud.Text = "" 'textvak ledigmaken txtInhoud.Text = LB.LeesBestand(cdlOpen.FileName) 'inhoud van text erin plaatsen LB = Nothing 'variabele ledigmaken End If End Sub
Code mnuopslaan Private Sub mnuOpslaan_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuOpslaan.Click If cdlSave.ShowDialog = Windows.Forms.DialogResult.OK Then Dim LB As New clsTxtReadWrite 'instantie maken van de klasse LB.SchrijfBestand(cdlSave.FileName, txtInhoud.Text) ' de inhoud van het tekstvak opslaan LB = Nothing 'variabele ledig maken End If End Sub
Code mnuKopieeren Private Sub mnuKopiëren_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuKopiëren.Click Clipboard.SetDataObject(txtInhoud.Text) End Sub
Code mnuKnippen Private Sub mnuKnippen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuKnippen.Click Clipboard.SetDataObject(txtInhoud.Text) txtInhoud.Text = "" End Sub
deze twee code zijn quasi het zelfde, met dien verstande dat bij ghet knippen de uiteindelijke tekst verdwijnt uit het tekstvak, in tegenstelling tot kopiëren. Code mnuPlakken Private Sub mnuPlakken_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuPlakken.Click Dim CB_lezer As IDataObject = Clipboard.GetDataObject() If CB_lezer.GetData(DataFormats.Text) Then txtInhoud.SelectedText = CB_lezer.GetData(DataFormats.Text) End If End Sub
40
rd Voor de volledigheid moeten we nog een code toeveoegen in het loadevent van ons formulier. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Document" + docteller.ToString docteller += 1 End Sub
Bij ons formulier gaan we nog iets voegen, namelijk een module. In een module kunnen we variabelen declareren die kunnen gebruikt worden doorheen gans het project, als men ze public gedeclareerd heeft. In dit geval zullen we docteller public declareren. Module Module1 Public docteller As Integer = 1 End Module
We gaan nu het nieuw formulier declareren. Het is mogelijk van een nieuw formulier te vormen in een formulier. Dit formulier kan zelfs hetzelfde zijn als dat waar we in bezig zijn. Dat is logisch want vergeet niet dat het formulier infeite een klasse is, en dus is het mogelijk een nieuwe instantie van die klasse te nemen en mee te werken. Private Sub mnuNieuwFormulier_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuNieuwFormulier.Click Dim newForm As New frmTekst newForm.Show() End Sub
We kunnen nu ons programma even uittesten. Alles werkt zoals verwacht, echter er zijn nog een paar vervelende zaken die er in verschijnen o Per formulier wordt er een koppeling gemaakt naar de startbalk van windows o Bij afsluiten van “document 1” worden automatisch alle documenten afgesloten. De reden hiervan is omdat alle formulieren gemaakt zijn vanuit 1 basis document, dus akls dat verdwijnt zullen ook alle andere verdwijnen. dit is niet iets dat we als “goede programmeur” mogen toelaten. We kunnen dit oplossen door te werken met MDI-bestanden. 17.3 MDI2 documenten. Als men met MDI werkt zullen alle formulieren die tot 1 projrct behoren binnen het zelfde kader gehouden worden. Dit is de manier waarop de meeste software werkt. U 2
MDI = Multiple Document Interface
41
rd kunt bijvoorbeeld bij het programma word weel neven paginas opendoen in dezelfde sessie. Deze zullen dan ook binnen het zelfde venster manifesteren. Let wel per programma kan men maar één MDI form hebben. Vaak wordt dit het parent form genoemd, de andere form zijn dan child forms. Het is niet de bedoeling om , behalve menu’s, besturingselementen op het MDI form te plaatsen. Het is dus een container (verzamelplaats) waarin de rets vna de formulieren gevangen gehouden worden. We gaan opnieuw het progrmma hierboven maken maar nu binnen in een MDI form. Maak een nieuw project aan. Hernoem form1 tot frmMDI. En de eigenschap tekst in Textverwerker. De eigenschap WindowsState zet je tot Maximized en de eigenschap IsMidiContainer tot True. Ogenblikkelijk zal de achtergrond kleur van het formulier grijs worden. Maak nu en mainmenu aan en plaast er de onderstaande gegevens in.
Fig. 36
42
rd MenuItem
Naam
Bestand
mnuBestand
Nieuw
mnuNieuw
Afsluiten
mnuAfsluiten
Venster
mnuVenster
Onderelkaar
mnuOnderelkaar
Naastelkaar
mnuNaastElkaar
Trapsgewijs
mnuTrapsGewijs
Schikken
mnuSchikken
Maak een nieuww formulier aan met de naam frmTekst. Plaats op dit formulier een tekstvak met de naam txtInhoud. Zet bij dat tekstvak Multiline op true en de scrollbar op vertical en Achor op Top, Bottom, Left, Right. Hierdoor zal de grootte van het tekstvak steeds de grootte van het formulier volgen. We maken ook terug een module aan met één publike variabele Module Module1 Public docteller As Integer = 1 End Module
Terug gaan we de klasse cls txtReadWrite aanmaken Public Class txtReadWrite Public Function LeesBestand(ByVal bestandsNaam As String) As String Dim SR As System.IO.StreamReader Dim retour As String If System.IO.File.Exists(bestandsNaam) Then SR = System.IO.File.OpenText(bestandsNaam) retour = SR.ReadToEnd() SR.Close() Else retour = "" End If SR = Nothing Return retour End Function Public Sub schrijfbestand(ByVal bestandsnaam As String, ByVal inhoud As String) Dim sw As System.IO.StreamWriter sw = System.IO.File.CreateText(bestandsnaam)
43
rd sw.Write(inhoud) sw.Flush() sw.Close() sw = Nothing End Sub End Class
We keren terug naar ons MDI bestand (frmMDI). Aan mnuNieuw hagen we volgende code Public Class frmMDI Private Sub mnuNieuw_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuNieuw.Click Dim f As New frmTekst f.MdiParent = Me f.Text = "document " & docteller.ToString f.WindowState = (FormWindowState.Maximized) docteller += 1 f.Show() End Sub End Class
Met deze code maakt u terug een objectvariabele en die verwijst nar het formulire welke we zopas aangemaakt hebben. Wij maken ook duidelijk dat het gemaakte formulier een child is van de parent frmMDI (in dit geval Me). In de titelbalk zorgen we terug dat het document een opeenvolgende nummering krijgt. Let wel we zorgen er ook voor dat het formulier gemaximaliseerd wordt. Het menu venster krijgt volgende code Private Sub OnderelkaarToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OnderelkaarToolStripMenuItem.Click Me.LayoutMdi(MdiLayout.TileHorizontal) End Sub Private Sub NaastelkaarToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles NaastelkaarToolStripMenuItem.Click Me.LayoutMdi(MdiLayout.TileVertical) End Sub Private Sub TrapgewijsToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrapgewijsToolStripMenuItem.Click Me.LayoutMdi(MdiLayout.Cascade) End Sub
44
rd Private Sub SchikkenToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SchikkenToolStripMenuItem.Click Me.LayoutMdi(MdiLayout.ArrangeIcons) End Sub
Omdat het menu venster voor alle mdi onderdelen gebruikt wordt hebben we speciale voorzieningen gemaakt voor dat menu. Dit laat toe de vensters te schikken in het parent venster.zoal u ziet hoef hje zelf neits te programmeren om de vensters te schikken in het hoofdvenster. Probeer maar eens je programma. 17.4 Mdi- en childmenu samenvoegen Het is de bedoeling, als je met Mdi forms werkt dat het hoofdmenu verschijnt op de parent form. En niet op de child forms Echter het menu dat we gemakt hebben is beperkt en voldoet niet aan de gevraagde noden. Echter heeft het ook geen zin om meer items in het menu te plaatsen. Als er geen gformulier te zien is hoeven de menus ook niet. Mischienb werken we wel met verschillende child formulieren zodat bij andere child formulieren de menu van geen belang zijn. Dan hebben we een speciale manuier om tewerk te gaan. We maken op het child formulier wel een menu systeem aan, met menu item voor de gegeven noden. Van zodra jez het child menu opend vanuit de MDI form worden de 2 menu’s (1 van de parent en 1 van de child form) samengevoegd (gemerged). Een voorbeeld: Plaats in frmTekst een neiuw menusyetem (mnchild) en breng de onderstaande zaken er in. menuItem
Naam
Bestand
Mnubestand
-
nvt
Openen
mnuOpen
Opslaan
mnuOpslaan
Opslaan als
mnuOpslaanAls
Sluiten
mnuSluiten
45
rd -
Nvt
Afdrukvoorbeeld
mnuAfdrukVoorbeeld
Afdrukken
mnuAfdrukken
Bewerken
mnuBewerken
Kopiëren
mnuKopiëren
Knippen
mnuKnippen
Plakken
mnuPlakken
Opmaak
mnuOpmaak LetterType
mnuLetterType
Tekstkleur
mnuTekstKleur
PginaKleur
M,uPaginaKleur
46
rd Wat
Naam
filter
Mainmenu
mnuMenu
OpenFileDialog
cdlOpenFile
txtBestanden|*.txt
SaveFileDialog
cdlSaveFile
txtBestanden|*.txt
FontDialog
cdlFont
ColorDialog
cdlColor
PrintDocument
PrintDoc
PrintDialog
cldprint
PrintPreviwDialog cldPrintPreview Document = PrintDoc
Om beide samen te voegen gaan we in het item menu op de mdi form de porperty menulist Item zoek u de naam van de list dat je wilt mergen (samenvoegen). Let wel op de benamingen.
Bij het openen van een nieuw document krijgen we dan onderstaande figuur in onze MdiForm. Zoals gezegd moet men wel letten op de benaming
47
rd
Hoofdstuk 18 ADODB 18.1 Enkele bedenkingen In dit gedeelte gaan we de mogelijkheid onderzoeken voor het werken met databanken in samenwerking met VB.net. ADO3 een samenstelling model objecten om databanken te bewerken. Het vormt een inleiding tot ActiveX Data Objects. ADO werd voor .net Framework volledig herschreven, en er zijn een aantal objecten gecreëerd die het werken met databanken gemakkelijker zou moeten maken. Microsoft heeft beloofd dat het wel vlotter zou moeten gaan, maar toch. Vroeger en ik denk nu nog werkt deze ADO enkel met losgekoppelde databanken. Voorbeelddatabank
Maak bovenstaande databank en sla hem op bij uw documenten. We gebruiken de normale Access indeling. 3
Component Object Model (COM) objects for accessing data sources. (Wikipedia)
48
rd
18.2 Wat is ADO ADO was in het begin bedoeld als een vervanging DAO, de oorspronkelijke manier waarop VB databanken benaderd. Hetzelfde als bij DAO zal met ADO de gegevens van de databanken benaderd worden op verschillende manieren. Hier volgend zullen we de verschillende stappen aangeven met bijhorende maatregelen wanneer we een ADO verbinding willen realiseren. 18.2.1 Verbindingswijzen ADO maakt het mogelijk om en database te benaderen uit het programma code. We moeten eerst een verbinding maken met de databank. Dit gebeurt door de OLE DBprovider. Deze OLE DB provider is het nieuwe low-level database interface van microsoft, die toegang verleent tot verschillende soorten gegevens. Er zijn OLE DB providers voor de traditionele databanken( SQL-Server), email servers…enz. De provider staat tot beschikking van ADO, die volgende verbindingen met de gegevens mogelijk maakt. Data-besturingselement. Een data-besturingselement is een aangepast besturingselement dat de communicatie met de databank verzorgt. Object Interface. Wanneer een verwijzing naar ADO opneemt, krijgt het programma beschikking tot nieuwe objecten. 18.2.2 Maken van een voorbeeld. We openen een nieuwe form en geven deze de naam klant. We gaan nu zorgen dat we een verbinding hebben met de database die we gemaakt hebben. Deze verbinding maken we in het menuItem Tools – connect to database
49
rd Na dat we dat gedaan hebben komen we een pop-up menu tegen waarin we onze verbinding zuilen verwezenlijken.
We zien bovenstaande figuur. We vullen in het gedeelte browse onze database in
In dit geval staat het op mijn F schijf. Let wel het bekende mdb is veranderd in accdb. Deze extensie voor het gebruik van een Access database in ADO bij VB.net. Nu gaan we in onze form een object bij tekenen namelijk dat DataGridView. Bij het tekenen van dit object op onze form krijgen we bepaalde properties welke we dienen aan te vullen 50
rd
Bij het aanduiden van onze datasource krijgen we volgende voorstelling
Bij het aangeven van Add Project Data Source krijgen we de Data Choose Wizard waarin we Database aanduiden
51
rd Bij deze aanduiding krijgen we terug een ander menu. Bij dit menu moeten we aanduiden welke data base we willen connecteren. Daar nemen we onze database cliente.accdb
Vervolgens vragen ze ons de dataconnectstring te benomen. We kunnen deze en naam geven die we willen. Daarna moeten we de elementen aanduiden die we willen voorstellen, in ons geval volledig de tabel.
Nadat we dat gedaan hebben krijgen we volgende figuur onderstaande figuur. Letten we nu wel op de drie onderstaande objecten namelijk Dataset, Bindingsource en table adapter. Als we nu ons programma ‘runnen’ zullen we (normaal) onderstaande figuur krijgen. Daarin zien we dat onze tabel die we gemaakt hadden volledig gepresenteerd wordt in ons programma.
52
rd
Zoals we zien hebben we de benamingen die we gegeven hebben in onze tabel. We kunnen echter die veldnamen aanpassen door de kolom te ‘editeren’ als we erin gaan staan met onze cursorwijzer en op de rechter muisknop drukken en zo volgende figuur bekomen waar we het kunnen editeren en veranderen. Probeer maar
Zoals u ziet gebeurt dat in HeaderText. Daarin kunnen we dit veranderen. Dit is een methode die bestaat maar dat we niet gaan gebruiken.
53
rd
Hoofdstuk 19 ADO.net (1) Hier zullen we de beginselen zien van het gebruik van ADO.net De databank toepassingen welke men maakt in vb.net, zijn cliënt/server toepassingen. In dit model is het e bedoeling om zoveel mogelijk het werk uithanden te nemen van de lokale pc en te laten verwerken op de server, en zodoende de pc enkel te belasten met de presentatie van de gegevens. In dit eerste deel gaan we echter gebruik maken van de lokale pc als server, en beperken we ons tot eenvoudige Access database op onze lokale pc. Later (in deel 2) gaan we gebruik maken van de views en stored procedures. We gebruiken een databank (computers.mdb), die op de server staat en dat je kunt downloaden. De relaties in de databank zijn zoals in de figuur opgesteld.
54
rd In de cursus gaan we er van uitgaan dat we de databank op volgende locatie bewaren: C:\computers.mdb We gaan eerst een nieuw project aanmaken. Daar we redelijk veel ‘form’s’ nodig zullen hebben vertrekken we met een MDI form. Maak een start form aan met de onderstaande indeling
BasisTabellen
mnuBasisTabellen Hardisks
mnuHardDisks
Memory
mnuMemory
Motherboards
mnuMotherboards
Processor
mnuProcessor
Memory Versie 2
mnuMemoryVersie2
Motherbords 2
mnuMotherBords2
Venster
mnuVenster Onder Elkaar
mnuOnderElkaar
Naast Elkaar
mnuNaastElkaar
Trapsgewijs
MnuTrapsGewijs
Afsluiten
mnuAfsluiten End
mnuEnd
55
rd De menu’s Venster en afsluiten kunnen we onmiddellijk programmeren Public Class start Private Sub mnuOnderelkaar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuOnderelkaar.Click Me.LayoutMdi(MdiLayout.TileHorizontal) End Sub Private Sub mnuNaastElkaar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuNaastElkaar.Click Me.LayoutMdi(MdiLayout.TileVertical) End Sub Private Sub mnuTrapsGewijs_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuTrapsGewijs.Click Me.LayoutMdi(MdiLayout.Cascade) End Sub Private Sub mnuEnd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuEnd.Click End End Sub End Class
19.1 De architectuur van ADO.Net. De client toepassing vraagt aan der server om gegevens die naar de client computer worden verstuurd en lokaal wordt opgeslagen. Gegevens worden bij de server opgevraagd door middel van SQL-instructies en store procedures. Het resultaat van deze bevraging (mogelijks met een gedeelte die uit de databank komt)wordt terug van de server naar het pc gestuurd waar ze verder worden verwerkt. Op dat moment wordt de verbinding verbroken. De cliënt werkt op dat moment autono0m verder met deze deelgegevens, volledig losgekoppeld van de server. Deze verzameling noemt men DATASET. U kunt de gegevens dan bewerken in het data-object, eventueel nieuwe gegevens toevoegen of verwijderen. Dit gebeurt dan losgekoppeld van de eigenlijke databank. Op het ogenblik dat u beslist data het moment daar is om deze gegevens die je in het dataobject veranderd hebt te laten bijwerken op de server, moet u natuurlijk de verbinding opnieuw maken met deze server waar de gegevens dan gesynchroniseerd worden. Natuurlijk is het mogelijk dat terwijl de gegevens zich in de dataset bevinden, kunnen andere gebruikers nieuwe rijen aan de tabellen van de database toevoegen. Zelfs bestaande rijen kunnen bewerkt worden. 56
rd U kunt van tevoren helaas niet weten of een van de rijen in de lokale dataset al dan niet afwijkt van de oorspronkelijke rij in de databank. Het moment dat u de database wil synchroniseren komt u dat te weet. Als u directe toegang nodig hebt op de databank, ik denk hier aan reservatie opdrachten allerhande is het ADO systeem ongeschikt. Microsoft heeft aangegeven, dat dit in de ‘nabije’ toekomst zou aangepast worden. In ons geval, echter, moeten we voor hij kan opgenomen worden bepaalde acties ondernemen. We moeten een verbinding maken met de databank. Dit gebeurt met het connection object hiermede wordt bepaald: - Soort van databank systeem je gebruikt(Access, Oracle, server…). - waar deze zich bevindt - welke de parameters zijn, zoals gebruikersnaam, paswoord … enz. Nadien heeft men data adapter nodig. Deze zal de communicatie verzorgen tussen de dataset en de databank. Het is ook deze die er voor zorgt dat de resultaten van een SQL instructie in de dataset terecht komt, en dat de gegevens uit de dataset terug gestuurd wordt, zodat die bijgewerkt wordt.
19.2 Het programmeren Voor dat we beginnen met het programmeren zullen we onze toolbox even moeten uitbreiden. Dit met bepaalde elementen. Zoals de OLEDBconnection. Deze zullen we gebruiken om de verbinding te verwezenlijken met de databank. Voeg een nieuwe form bij het project en noem die frmMemory. Verander de form text in geheugen versie 1. Zet zowel maximize-box als minimize-box op false. Selecteer in de toolbox data het deel OLEDBConnection, en sleep het op uw formulier. U zult zien dat het zal gepositioneerd worden onderaan uw form. Verander de naam in Connectie. Open daarna het eigenschapvenster op ConnectionString. Als tweede voegen we ook OLEDBDataAdapter toe op de zelfde wijze
57
rd
Neem het gedeelte “New Connection”. Een wizard wordt gestart waarop we invullen dat we met een Access database werken. Direct daarop verschijnt een andere wizard waar we zien dat we onze databank kunnen invoeren. Tevens, en dit om dat we geen beveiliging op onze databank hebben, houden we als usernaam Admin en laten het paswoord vrij. Met de knop test connection kunnen we ons vergewissen indien we verbinding hebben met de databank.
Vervolgens gaan we een data adapter invoegen. Deze zal terug onderaan terecht komen. Automatisch wordt er een wizard opgestart
58
rd Op het scherm staat de connectie die je al gemaakt hebt in de vorige OLEDBconnection. Klik dan op next
Aangezien de data adapter de communicatie verzorgt tussen de databank en het programma. De dataadapter doet dit met behulp van SQL instructies. Let wel aangezien we hier werken met Access zijn de mogelijkheden van stored procedures hier niet van werking
In het volgende scherm kan je deze SQL instructies inbrengen die de data adapter nodig heeft. Echter bij voorkeur gaan we de querybuilder gebruiken. Dus klik op query builder
59
rd In eerste instantie wordt er een lijst van de tabellen gegeven die van de besproken databank is. Daarin selecteren we Memory
Zet bij alle velden een vinkje. Desgewenst kan men ook een volgorde instellen en klik op OK
U kunt dan verder gaan tot dat je op het latset venster uitkomt en dan klik je op finish
60
rd Zorg dat de OLEDBDataAdapter geselecteerd staat, en verander zijn naam in adapterMemory. Klik in het menu op data en dan op Generate Dataset In het scherm dat dan verschijnt selecteert u new en voert als naam in EersteMemorySet en druk dan op OK Onderaan verschijnt er nu een dataset.
Als u de eigenschappen van de dataset bekijkt zal er iets opvallen Enerzijds heb je als datasetname EersteMemorySet, maar je hebt ook als naam van de dataset EersteMemorySet1. Een dataset is een object de men creëert en dat over gans het project meeloopt. EersteMemorySet is het object en anderzijds is eersteMemorySet1 een instantie van dit object. Zet nu een datagrid op je form en noem het DGRMemory. Om je dataset nu te koppelen aan deze grid heb je twee mogelijkheden. Men stelt de DataSource op EersteMemorySet1 en de datamember op Memory, of men stelt de eigenschap DataSource direct op EersteMemorySet1.Memory. Als men nu het programma start zal men zien dat men nog niets ziet. Hoewel de kolom koppen al vermeld staan is er niets van data. De reden daarvoor is omdat de data set nog niet gevuld is met data! Omdat te doen zal men in het LoadEvent van uw formulier de onderstaande code invullen. Deze code zal zorgen dat je dataset gevuld wordt. 61
rd Private Sub frmMemory_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load EersteMemorySet1.clear() AdapterMemory.Fill(EersteMemorySet1) End Sub.
Het moment dat je nu het programma opstart zal je zien dat de datagrid gevult is. Voor het eventueel formateren van de grid kan men de kolommen aanpassen. Dit kan je doen als je het grid selecteerd en met rechter muisknop drukt. Op dat moment kan je met edit coloms de kolomen aanpassen. Probeer maar eens. We kunnen nu al veel doen maar we kunnen nog niet de wijzegingen in het datacomplex bewaren. Dit doen we met volgende code die we in ons form_Closing Event van het formulier plaatsen. Private Sub frmMemory_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing AdapterMemory.Update(EersteMemorySet1) End Sub
Met deze code worden de veranderingen weggeschreven naar uw databank.
19.3 opdracht Maak de volgende formullieren aan, analoog met de bovenstaande. Zorg dat je met deze formulieren Harddisk, Motherboard en Processor kunt beheren.
62
rd
19.4 bewegen door de records Maak een nieuw formulier aan: frmMemry2 en plaats de controles zoals op voorbrrld.
txtMem_Grootte txtMen_Snelheid btnFirst btnPrevious btnNext btnLast lblPosition
Plaats een OLEdbadapter op uw formulier. En stel die op. Direct wordt er een dataconnectie gemaakt. Deze connectie zal natuurlijk spreken op computers.mdb. we noemen deze connection connectie en de adapter MemoryAdaptor.
63
rd Als je nu een dataset laat genereren neem dan EersteMemorSet1 als dataset (hij bestaat al).
Bij de eigenschappen van de tekstvakken moeten we aan de databindingen volgende toevoegen. Voor de Grootte gaan we naar de respectievelijke grootte in de databank en voor de snelheid natuurlijk het zelfde. Om nu ons systeem te laten werken moeten we natuurlijk de nodige data in onze adapter vullen. Daarom vullen we volgende codes op de respectievelijke plaatsen in: Public Class frmMemory2 Dim positie As Long Dim RecAantal As Long
Private Sub frmMemory2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load EersteMemorySet1.Clear() MemoryAdapter.Fill(EersteMemorySet1) RecAantal = Me.BindingContext(EersteMemorySet1, "Memory").Count - 1 If RecAantal = -1 Then btnPrevious.Enabled = False btnNext.Enabled = False btnFirst.enabled = False btnLast.Enabled = False Else positie = 0 Me.BindingContext(EersteMemorySet1, "Memory").Position = 0 lblPosition.Text = (positie + 1).ToString & "/" & (RecAantal + 1).ToString End If End Sub Private Sub btnFirst_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFirst.Click positie = Me.BindingContext(EersteMemorySet1, "Memory").Position positie = 0 Me.BindingContext(EersteMemorySet1, "Memory").Position = 0 lblPosition.Text = (positie + 1).ToString & "/" & (RecAantal + 1).ToString End Sub Private Sub btnLast_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLast.Click positie = Me.BindingContext(EersteMemorySet1, "Memory").Position positie = RecAantal Me.BindingContext(EersteMemorySet1, "Memory").Position = RecAantal lblPosition.Text = (positie + 1).ToString & "/" & (RecAantal + 1).ToString End Sub
64
rd
Private Sub btnPrevious_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPrevious.Click positie = Me.BindingContext(EersteMemorySet1, "Memory").Position If positie > 0 Then positie -= 1 Me.BindingContext(EersteMemorySet1, "Memory").Position = positie End If lblPosition.Text = (positie + 1).ToString & "/" & (RecAantal + 1).ToString End Sub Private Sub btnNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNext.Click positie = Me.BindingContext(EersteMemorySet1, "Memory").Position If positie < RecAantal Then positie += 1 Me.BindingContext(EersteMemorySet1, "Memory").Position = positie End If lblPosition.Text = (positie + 1).ToString & "/" & (RecAantal + 1).ToString End Sub End Class
Het enige wat ons nu nog rest is te zorgen dat dit foremulier de database bijwerkt. Dit gebeurt in het closing event. Private Sub frmMemory2_Clossing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing MemoryAdapter.Update(EersteMemorySet1) End Sub
65
rd
Hoofdstuk 20 Benaderen van de databanken vanuit de code (ADO2) In voorgaande uitleg hebben we steeds de databanken benaderd van uit een soort wizards. Deze zijn gemaakt om databanken te benaderen waar men zeker is dat deze nooit veranderen van plaats of van indeling. Zoals u weet ben ik niet altijd te vinden voor het gebruik van wizards. Deze zijn meestal van het principe ‘monkey see, monkey do’ en dat legt een beperking op aan de vrijheid van de programmeur. Daarom gaan we vanaf nu proberen het werken met een databank zelf in handen te nemen.
20.1 verbindingen met de databank Het idee is het maken van onze verbindingen met behulp van de controles gaan vanaf nu niet meer doen maar we gaan deze verwezenlijken via de code. Deze code zal rechtstreeks in onze formulieren geprogrammeerd worden. Het zal de bedoeling zijn om in de volgende bespreking alles wat handelt over ADO netjes in een klasse te gieten (dit om te vermijden dat we steeds alles opnieuw moeten programmeren, want dat was immers de oorspronkelijke bedoeling van gebruik van een klasse). Maar om het idee eerst eens ‘aan den lijve’ te ondervinden gaan we eerst eens alles zelf programmeren, zonder gebruik te maken van een klasse. Maak daarvoor onderstaand formulier:
66
rd De componenten zijn: Object
Opschrift
Naam
Groepbox
Data
grpData
Listbox Knop
LST Add
btnAdd
Edit
btnEdit
Del
btnDel
Groepbox
Details
grpDetails
Textveld
Merknaam
txtMerknaam, MaxLangth 20
Snelheid
txtSnelheid, maxLength 10
Grootte
txtGrootte, textAllign right
Buffer
txtBuffer, textallign right
Save
btnSave, visible false
Undo
btnUndo, visible false
Button
LST wordt gevuld met algemene informatie die de gebruiker instaat stelt een element uit de tabel te selecteren. Als de gebruiker een item geselecteerd heeft verschijnt de info i.v.m. het element in de groepbox Details. Klikt de gebruiker op Add of Edit dan wordt de rechterkant van het scherm ontoegankelijk. De linkerkant echter wordt toegankelijk zodat de gebruiker een nieuw item kan invoeren, of een bestaand item bewerken. Bij cancel wordt rechts weer toegankelijk en links niet toegankelijk. Er gebeurt verder NIETS Bij Save dan kunnen er twee mogelijkheden zich voordoen. Heeft de gebruiker een nieuw item ingevoerd wordt hij toegevoegd bij de bestaande tabel. Bij een wijziging wordt de wijziging opgenomen in de tabel. Opnieuw wordt links toegankelijk en rechts ontoegankelijk Bij Del zal het record verwijderd worden. 67
rd
20.2 Publieke variabelen Een aantal variabelen moeten gekend zijn voor gans het formulier. Daarvoor zullen we deze opnemen als publieke variabelen. -
In de “connectiestring” zullen we de gegevensplaatsen om een verbinding te maken. In ons geval met een Access databank maar dat kan ook voor een andere i.e. sql…enz
-
“SQL instructie” zullen wij gebruiken om ,met behulp van de sql taal, op te geven welke informatie in de databank moet opgehaald worden.
-
In dit formulier gaan we 2 data-sets en 2 data-adapters gebruiken. Eén om de informatie af te beelden in de listbox en één om de tabel bij te werken indien nodig. DS en DA gebruiken we om de tabel te beheren, DSListbox en DAListbox om de listbox te bevolken.
-
Tevens hebben we een CommandBuilder nodig om records toe te voegen, te wijzigen of te verwijderen uit de tabel. Om dat we maar 1 adapter hebben om in de tabel te werken (DA), zullen we ook maar 1 CommandBuilder gebruiken. De CommandBuilder zal zelf de SQL instructies schrijven om de gegevens toe te voegen, wijzigen of verwijderen. Het is belangrijk te weten dat u zelf er moet voor zorgen dat de SQL instructies waarmee u de adapters bevolkt die u gebruikt om de tabel zelf te beheren, steeds de primaire sleutel van de tabel zelf moet bevatten. Voor alle andere adapters is dit niet nodig Volgende code wordt aan uw form toegevoegd: Dim Dim Dim Dim Dim Dim Dim Dim
Toevoegen As Boolean ConnectieString As String SqlInstructie As String DS As New DataSet DSListbox As New DataSet DA As New OleDb.OleDbDataAdapter DAListBox As New OleDb.OleDbDataAdapter CmdBuilder As New OleDb.OleDbCommandBuilder
20.2 Het Load-Event. In het load-event van het formulier gaan we de respectievelijke adapters en datasets initialiseren en de listbox vullen. Omdat het vullen van de datasets en adapters op andere plaatsen ook zal moeten gebeuren maken we hier afzonderlijke methoden van! 68
rd Bekijk de onderstaande code: Private Sub frmHarddisk2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Call VulData() Call VulLst() End Sub Private Sub VulData() ConnectieString = "Provider= Microsoft.Jet.Oledb.4.0;Data Source= f:\computers.mdb;" SqlInstructie = "select hd_id, merknaam, snelheid, grootte, buffer from harddisk order by merknaam, snelheid, grootte" DA = New OleDb.OleDbDataAdapter(SqlInstructie, ConnectieString) DS.Clear() DA.Fill(DS) CmdBuilder = New OleDb.OleDbCommandBuilder(DA) SqlInstructie = "select merknaam + ' '+snelheid+'('+ format(grootte, '000')& ' GB)' as veld from harddisk order by merknaam, snelheid, grootte" DAListBox = New OleDb.OleDbDataAdapter(SqlInstructie, ConnectieString) DSListbox.Clear() DAListBox.Fill(DSListbox) Lst.Refresh() End Sub Private Sub VulLst() Dim T As Integer Lst.Items.Clear() For T = 0 To DSListbox.Tables(0).Rows.Count - 1 Lst.Items.Add(DSListbox.Tables(0).Rows(T).Item(0)) Next End Sub
20.3 Bij het klikken in de listbox Als een gebruiker een item in de listbox selecteert, dan dienen de corresponderende detailgegevens te verschijnen in het rechter gedeelte van het scherm. U kunt een record selecteren met de uitdrukking: Dataset.Tables().Rows().Item() -
Tables() bepaalt welke tabel uit de dataset moet gebruikt worden(die is voorlopig steeds de eerste tabel).
-
Rows() bepaalt hoeveelste record er moet geselecteerd worden binnen de tabel. We hebben er voor gezorgd dat de volgorde waarin de items worden afgebeeld in de ListBox overeenkomen met de volgorde waarin de records zich in de dataset bevinden (gebruik van de order by clausule). 69
rd Als we dus het 3de element uit de listbox selecteren zullen we spreken over het 3de element in de dataset -
Item() gaat over het veld binnen het geselecteerde record. Als we schrijven Item(1), dan spreken we over het 2de veld (hier Merknaam). Men mag ook schrijven Item(“merknaam”). Bekijk onderstaande code Private Sub Lst_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Lst.SelectedIndexChanged txtMerkNaam.Text = DS.Tables(0).Rows(Lst.SelectedIndex).Item(1) txtSnelheid.Text = DS.Tables(0).Rows(Lst.SelectedIndex).Item(2) txtGrootte.Text = DS.Tables(0).Rows(Lst.SelectedIndex).Item(3) txtBuffer.Text = DS.Tables(0).Rows(Lst.SelectedIndex).Item(4) End Sub
20.4 btnAdd Het principe is gekent uit vorige opmerking. Links wordt ontooegankelijk gemaakt, rechts toegankelijk. Gezien dat we spreken over een neiuw record worden alle valden ledig gemaakt. De publieke variabele Toevoegen wordt op TRUE gezet zodat we straks bij het bewaren nog weten dat het om een toevoeging of wijziging gaat. Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click Toevoegen = True grpData.Enabled = False grpDetails.Enabled = True btnSave.Visible = True btnCancel.Visible = True txtMerkNaam.Clear() txtSnelheid.Clear() txtGrootte.Clear() txtBuffer.Clear() txtMerkNaam.Focus() End Sub
20.5 btnEdit Hetzelfde als hierboven maar nu worden de tekstvakken NIET leeggemaakt en wordt de variabele Toevoegen op False gezet Private Sub btnEdit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEdit.Click If Lst.SelectedIndex = -1 Then Exit Sub Toevoegen = False grpData.Enabled = False
70
rd grpDetails.Enabled = True btnSave.Visible = True btnCancel.Visible = True txtMerkNaam.Focus() End Sub
20.6 btnCancel Hier dient net het omgekeerde gebeuren als bij btnAdd en btnEdit Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click grpData.Enabled = True grpDetails.Enabled = False btnSave.Visible = False btnCancel.Visible = False Lst.Focus() Call Lst_SelectedIndexChanged(sender, e) End Sub
20.7 btnSave Om een record toe te voegen vanuit de code aan uw tabele heeft u een DataRowCollection en een DataRow nodig. De eerste keer dat je die code ziet zal ze wel ietwat vreemd overkomen, maar ze is logisch te begrijpen. Het wijzigen gebeurt rechtstreeks op de item-eigenschappen van de tabel. Om het programma niet te veel te verzwaren zullen we ons niets aantrekken van de controle op het inhoudelijke. Het is wel zo dat dit normaal moet gebeuren, om zeker te zijn dat het programma “fools proof “ is. Om de wijzigingen/toevoegingen door te voeren naar de tabel moeten we nu de ComandBuilder zijn werk laten doen. U zult opmerken dat we niet expliciet een Insert of Update instructie te schrijven. De CommandBuilder zal dit voor u op de achtergrond doen. Er wordt nogmaals op benadrukt dat de primaire sleutel moet aanwezig zijn in de adapter! Omdat een data-adapter en een dataset statische gegevens zijn(dus losgekoppeld van de eigenlijke databank) moeten we deze nu laten updaten, afsluiten en terug openen. Dat is de reden waarom we de methode vuldata teug aanroepen. Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click Dim RowColl As DataRowCollection Dim NieuweRij As DataRow Dim Waarden(4) As Object Dim Selector As String
71
rd If Toevoegen = True Then RowColl = DS.Tables(0).Rows ' waarden(0) niet invullen want deze zijn autonummering Waarden(1) = txtMerkNaam.Text Waarden(2) = txtSnelheid.Text Waarden(3) = Integer.Parse(txtGrootte.Text) Waarden(4) = Integer.Parse(txtBuffer.Text) NieuweRij = RowColl.Add(Waarden) Else DS.Tables(0).Rows(Lst.SelectedIndex).Item(1) = txtMerkNaam.Text DS.Tables(0).Rows(Lst.SelectedIndex).Item(2) = txtSnelheid.Text DS.Tables(0).Rows(Lst.SelectedIndex).Item(3) = Integer.Parse(txtGrootte.Text) DS.Tables(0).Rows(Lst.SelectedIndex).Item(4) = Integer.Parse(txtBuffer.Text) End If Selector = txtMerkNaam.Text + " " + txtSnelheid.Text + " " + " (" _ + Format(txtGrootte.Text, "000") & "GB)" 'de selector wordt gebruikt om het huidige record te selecteren in de listbox DA.UpdateCommand = CmdBuilder.GetUpdateCommand DA.Update(DS.Tables(0)) grpData.Enabled = True grpDetails.Enabled = False btnSave.Visible = False btnCancel.Visible = False Lst.Focus() Lst.SelectedItem = Selector End Sub
20.8 btnDelete Met Rows(.delete method verwijdert u een rij uit de dataset. Denk er aan dat opnieuw de CommandBuilder zijn werk moet doen en daardoor u de dataset en data-adapter opnieuw moet sluiten en terug openen om een juist beeld te krijgen van de onderliggende tabel. Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click If Lst.SelectedIndex = -1 Then Exit Sub If MessageBox.Show("Mag dit onderdeel verwijderd worden?", "wissen", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) = DialogResult.Yes Then DS.Tables(0).Rows(Lst.SelectedIndex).Delete() DA.UpdateCommand = CmdBuilder.GetUpdateCommand DA.Update(DS.Tables(0)) Call VulData() Call VulLst() End If End Sub
72
rd 20.9 oefening Maak nu zelf een formulier voor de moederborden (frmMotherboards2). Gebruik daarvoor onderstaande form
73