Skip to content

5. Grafische interfaces met VB.NET en VS.NET

Hier laten we zien hoe je grafische interfaces kunt bouwen met VB.NET. Eerst bekijken we welke basisklassen van het .NET-platform we kunnen gebruiken om een grafische interface te bouwen. In eerste instantie gebruiken we geen tools voor automatische generatie. Vervolgens zullen we Visual Studio.NET (VS.NET) gebruiken, een ontwikkeltool van Microsoft die het ontwikkelen van applicaties met de .NET-talen vergemakkelijkt, en met name het bouwen van grafische interfaces. De gebruikte versie VS.NET is de Engelse versie.

5.1. De basisprincipes van grafische gebruikersinterfaces

5.1.1. Een eenvoudig venster

Laten we eens kijken naar de volgende code:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System
Imports System.Drawing
Imports System.Windows.Forms

' de formulierklasse
Public Class Form1
    Inherits Form

    ' de fabrikant
    Public Sub New()
        ' venstertitel
        Me.Text = "Mon premier formulaire"
        ' afmetingen van het venster
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub

    ' testfunctie
    Public Shared Sub Main(ByVal args() As String)
        ' het formulier wordt weergegeven
        Application.Run(New Form1)
    End Sub
End Class

De bovenstaande code wordt gecompileerd en vervolgens uitgevoerd

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm1.vb

dos>frm1

Tijdens de uitvoering wordt het volgende venster weergegeven:

Image

Een grafische interface is doorgaans afgeleid van de basisklasse System.Windows.Forms.Form:

Public Class Form1
    Inherits Form

De basisklasse Form definieert een basisvenster met knoppen voor sluiten, vergroten/verkleinen, een aanpasbare grootte, ... en beheert de gebeurtenissen op deze grafische objecten. Hier specialiseren we de basisklasse door er een titel en de breedte (300) en hoogte (100) aan toe te wijzen. Dit gebeurt in de constructor:


    Public Sub New()
        ' venstertitel
        Me.Text = "Mon premier formulaire"
        ' afmetingen van het venster
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub

De titel van het venster wordt bepaald door de eigenschap Text en de afmetingen door de eigenschap Size. Size is gedefinieerd in de naamruimte System.Drawing en is een structuur. De procedure Main start de grafische toepassing als volgt:


        Application.Run(New Form1)

Er wordt een formulier van het type Form1 aangemaakt en weergegeven, waarna de applicatie luistert naar gebeurtenissen die op het formulier plaatsvinden (muisklikken, muisbewegingen, ...) en de gebeurtenissen uitvoert die door het formulier worden ondersteund. In dit geval verwerkt ons formulier geen andere gebeurtenissen dan die welke worden verwerkt door de basisklasse Form (klikken op knoppen voor sluiten, vergroten/verkleinen, het venster vergroten of verkleinen, het venster verplaatsen, enz.).

5.1.2. Een formulier met een knop

Laten we nu een knop aan ons venster toevoegen:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System
Imports System.Drawing
Imports System.Windows.Forms

' de formulierklasse
Public Class Form1
    Inherits Form

    ' attributen
    Private cmdTest As Button

    ' de constructor
    Public Sub New()
        ' de titel
        Me.Text = "Mon premier formulaire"
        ' de afmetingen
        Me.Size = New System.Drawing.Size(300, 100)
        ' een knop
        ' aanmaken
        Me.cmdTest = New Button
        ' positie
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' grootte
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' tekst
        cmdTest.Text = "Test"
        ' gebeurtenisbeheerder
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' knop aan formulier toevoegen
        Me.Controls.Add(cmdTest)
    End Sub

    ' gebeurtenisbeheerder
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' er is op de knop geklikt – dit wordt gemeld
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    ' testfunctie
    Public Shared Sub Main(ByVal args() As String)
        ' het formulier wordt weergegeven
        Application.Run(New Form1)
    End Sub
End Class

We hebben een knop aan het formulier toegevoegd:


        ' een knop
        ' aanmaken
        Me.cmdTest = New Button
        ' positie
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' grootte
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' tekst
        cmdTest.Text = "Test"
        ' gebeurtenisbeheerder
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' knop aan formulier toevoegen
        Me.Controls.Add(cmdTest)

De eigenschap Location stelt de coördinaten (110,20) van de linkerbovenhoek van de knop in met behulp van een structuur Point. De breedte en hoogte van de knop worden ingesteld op (80,30) met behulp van een structuur Size. Met de eigenschap Text van de knop kan de tekst van de knop worden ingesteld. De klasse ‘knop’ heeft een Click-gebeurtenis die als volgt is gedefinieerd:

Public Event Click As EventHandler

waarbij EventHandler een „gedelegeerde” functie is met de volgende signatuur:

Public Delegate Sub EventHandler(ByVal sender As Object,ByVal e As EventArgs)

Dit betekent dat de gebeurtenishandler [Click] voor de knop de handtekening van de gedelegeerde functie [EventHandler] moet hebben. Wanneer hier op de knop cmdTest wordt geklikt, wordt de methode cmdTest_Click aangeroepen. Deze is als volgt gedefinieerd, in overeenstemming met het voorgaande sjabloon EventHandler:


    ' gebeurtenisbeheerder
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' er is op de knop geklikt – dit wordt gemeld
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

Er wordt alleen een bericht weergegeven:

Image

De klasse wordt gecompileerd en uitgevoerd:

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4

dos>frm2

De klasse MessageBox wordt gebruikt om berichten in een venster weer te geven. We hebben hier de constructor gebruikt

Overloads Public Shared Function Show(ByVal owner As IWin32Window,ByVal text As String,ByVal caption As String,ByVal buttons As MessageBoxButtons,ByVal icon As MessageBoxIcon) As DialogResult
text
het weer te geven bericht
caption
de titel van het venster
buttons
de knoppen in het venster
icon
het pictogram in het venster

De parameter buttons kan de volgende constantewaarden aannemen:

constante
knoppen
   AbortRetryIgnore 
  OK 
    OKCancel 
    RetryCancel 
    YesNo 
    YesNoCancel 

De parameter icon kan een van de volgende constante waarden aannemen:

Asterisk
Error
idem Stop
Exclamation
idem Waarschuwing
Hand
Information
idem Asterisk
None
Question
Stop
idem Hand
Warning

 

De methode Show is een statische methode die een resultaat van het type System.Windows.Forms.DialogResult retourneert, wat een opsomming is:


public enum System.Windows.Forms.DialogResult 
{
    Abort = 0x00000003, 
    Cancel = 0x00000002, 
    Ignore = 0x00000005, 
    No = 0x00000007, 
    None = 0x00000000, 
    OK = 0x00000001, 
    Retry = 0x00000004, 
    Yes = 0x00000006, 
}

Om te weten op welke knop de gebruiker heeft gedrukt om het venster van het type MessageBox te sluiten, schrijven we:

dim res as DialogResult=MessageBox.Show(..)
if res=DialogResult.Yes then
' er is op de knop 'Ja' gedrukt
...
end if

5.2. Een grafische interface bouwen met Visual Studio.NET

We nemen enkele van de eerder besproken voorbeelden opnieuw onder de loep en bouwen ze nu met Visual Studio.NET.

5.2.1. Het project aanmaken

  1. Start VS.NET en selecteer de optie Fichier/Nouveau/Projet

Image

  1. Voer de specificaties van uw project in
    1. selecteer het type project dat u wilt bouwen, in dit geval een VB.NET-project (1)
    2. selecteer het type applicatie dat u wilt bouwen, in dit geval een Windows-applicatie (2)
    3. geef aan in welke map u de submap van het project wilt plaatsen (3)
    4. Geef de naam van het project op (4). Dit wordt ook de naam van de map waarin de projectbestanden worden opgeslagen
    5. De naam van deze map wordt weergegeven in (5)
  1. Er worden vervolgens een aantal mappen en bestanden aangemaakt in de map i4:
submappen van de map project1

Van deze bestanden is er slechts één van belang, namelijk het bestand form1.cs. Dit is het bronbestand dat hoort bij het formulier dat is aangemaakt door VS.NET. We komen hier later op terug.

5.2.2. De vensters van de interface van VS.NET

De interface van VS.NET toont nu bepaalde elementen van ons project i4:

We hebben een ontwerpvenster voor de grafische interface:

Door besturingselementen uit de werkbalk (toolbox 2) te selecteren en deze op het vensteroppervlak (1) te plaatsen, kunnen we een grafische interface bouwen. Als we de muis over de "toolbox" bewegen, wordt deze vergroot en verschijnen er een aantal besturingselementen:

Image

Voorlopig gebruiken we er nog geen. Nog steeds op het scherm van VS.NET vinden we het venster van de oplossingsverkenner "Explorer Solution":

Image

In eerste instantie zullen we dit venster nog niet veel gebruiken. Het toont alle bestanden waaruit het project bestaat. Slechts één daarvan is voor ons van belang: het bronbestand van ons programma, in dit geval Form1.vb. Door met de rechtermuisknop op Form1.vb te klikken, verschijnt er een menu waarmee we toegang krijgen tot de broncode van onze grafische interface (Code weergeven) of tot de grafische interface zelf (View Designer):

Image

Je kunt deze twee entiteiten rechtstreeks openen vanuit het venster ‘Solution Explorer’:

 

De geopende vensters ‘stapelen’ zich op in het hoofdontwerpvenster:

Image

Hier verwijst Form1.vb[Design] naar het ontwerpvenster en Form1.vb naar het codevenster. U hoeft alleen maar op een van de tabbladen te klikken om van het ene venster naar het andere te schakelen. Een ander belangrijk venster dat op het scherm van VS.NET te zien is, is het eigenschappenvenster:

Image

De eigenschappen die in het venster worden weergegeven, zijn die van het besturingselement dat momenteel is geselecteerd in het grafische ontwerpvenster. Via het menu Affichage krijgt men toegang tot verschillende vensters van het project:

Image

Hier vindt u de zojuist beschreven hoofdvensters en de bijbehorende sneltoetsen.

5.2.3. Een project uitvoeren

Hoewel we nog geen code hebben geschreven, hebben we al een uitvoerbaar project. Voer F5 of Déboguer/Démarrer uit om het project uit te voeren. We krijgen dan het volgende venster te zien:

Image

Dit venster kan worden vergroot, geminimaliseerd, in grootte worden aangepast en gesloten.

5.2.4. De door VS.NET gegenereerde code

Laten we eens kijken naar de code (Affichage/Code) van onze applicatie:


Public Class Form1
    Inherits System.Windows.Forms.Form

#Regio "Code gegenereerd door de Windows Form Designer"

    Public Sub New()
        MyBase.New()

        'Deze aanroep is vereist door de Windows Form Designer.
        InitializeComponent()

        'Voeg een willekeurige initialisatie toe na de aanroep InitializeComponent()

    End Sub

    'De vervangen methode verwijdert het formulier om de lijst met componenten op te schonen.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Vereist door de Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'REMARQUE: de volgende procedure is vereist door de Windows Form Designer
    'Deze kan worden gewijzigd met behulp van de Windows Form Designer.  
    'Wijzig deze niet met behulp van de code-editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.Text = "Form2"
    End Sub

#Einde regio

End Class

Een grafische interface is afgeleid van de basisklasse System.Windows.Forms.Form:

Public Class Form1
    Inherits System.Windows.Forms.Form

De basisklasse Form definieert een basisvenster met knoppen voor sluiten, vergroten/verkleinen, een aanpasbare grootte, ... en beheert de gebeurtenissen op deze grafische objecten. De constructor van het formulier maakt gebruik van een methode InitializeComponent waarin de besturingselementen van het formulier worden aangemaakt en geïnitialiseerd.


    Public Sub New()
        MyBase.New()

        'Deze aanroep is vereist door de Windows Form Designer.
        InitializeComponent()

        'Voeg een willekeurige initialisatie toe na de aanroep InitializeComponent()
    End Sub

Alle andere taken in de constructor kunnen worden uitgevoerd na het aanroepen van InitializeComponent. De methode InitializeComponent


Private Sub InitializeComponent()
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 53)
        Me.Name = "Form1"
        Me.Text = "Form1"
    End Sub

stelt de titel van het venster "Form1", de breedte (292) en de hoogte (53) vast. De titel van het venster wordt bepaald door de eigenschap Text en de afmetingen door de eigenschap Size. Size is gedefinieerd in de naamruimte System.Drawing en is een structuur. Om deze toepassing uit te voeren, moeten we de hoofdmodule van het project definiëren. Hiervoor gebruiken we de optie [Projets/Propriétés]:

Image

In [Objet de démarrage] geven we [Form1] op, het formulier dat we zojuist hebben aangemaakt. Om de uitvoering te starten, gebruiken we de optie [Déboguer/Démarrer]:

Image

5.2.5. Compileren in een DOS-venster

Laten we nu eens proberen deze toepassing te compileren en uit te voeren in een DOS-venster:

dos>dir form1.vb
14/03/2004  11:53               514 Form1.vb

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

vbc : error BC30420: 'Sub Main' est introuvable dans 'Form1'.

De compiler geeft aan dat hij de procedure [Main] niet kan vinden. Dit komt doordat VS.NET deze niet heeft gegenereerd. We zijn deze echter al tegengekomen in de voorgaande voorbeelden. Ze heeft de volgende vorm:


    Shared Sub Main()
        ' de app wordt gestart
        Application.Run(New Form1) ' où Form1 est le formulaire
    End Sub

Laten we de bovenstaande code toevoegen aan de code van [Form1.vb] en opnieuw compileren:

dos>vbc /r:system.dll /r:system.windows.forms.dll form2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4

Form2.vb(41) : error BC30451: Le nom 'Application' n'est pas déclaré.

        Application.Run(New Form2)
        ~~~~~~~~~~~

Deze keer is de naam [Application] niet bekend. Dit betekent simpelweg dat we de naamruimte [System.Windows.Forms] niet hebben geïmporteerd. Laten we de volgende instructie toevoegen:

Imports System.Windows.Forms

en vervolgens opnieuw compileren:

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

Deze keer lukt het wel. Laten we het volgende uitvoeren:

dos>dir
14/03/2004  11:53               514 Form1.vb
14/03/2004  12:15             3 584 Form1.exe

dos>form1

Image

Er wordt een formulier van het type Form1 aangemaakt en weergegeven. Het toevoegen van de procedure [Main] kan worden vermeden door de optie /m van de compiler te gebruiken, waarmee de uit te voeren klasse kan worden gespecificeerd in het geval dat deze afstamt van System.Windows.Form:

dos>vbc /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /m:form2 form2.vb

De optie /m:form2 geeft aan dat de uit te voeren klasse de klasse met de naam [form2] is.

5.2.6. Gebeurtenisafhandeling

Zodra het formulier wordt weergegeven, luistert de toepassing naar gebeurtenissen die op het formulier plaatsvinden (klikken, muisbewegingen, ...) en laat ze uitvoeren die door het formulier worden afgehandeld. In dit geval ondersteunt ons formulier geen andere gebeurtenissen dan die welke worden ondersteund door de basisklasse Form (klikken op knoppen voor sluiten, vergroten/verkleinen, wijzigen van de venstergrootte, verplaatsen van het venster, ...). Het gegenereerde formulier maakt gebruik van een attribuut components dat nergens wordt gebruikt. De methode dispose heeft hier eveneens geen functie. Hetzelfde geldt voor bepaalde gebruikte naamruimten (Collections, ComponentModel, Data) en voor de naamruimte die voor het project is gedefinieerd: projet1. Daarom kan de code in dit voorbeeld worden vereenvoudigd tot het volgende:


Imports System
Imports System.Drawing
Imports System.Windows.Forms

Public Class Form1
    Inherits System.Windows.Forms.Form

    ' constructor
    Public Sub New()
        ' het formulier wordt opgebouwd met zijn componenten
        InitializeComponent()
    End Sub

    Private Sub InitializeComponent()
        ' venstergrootte
        Me.Size = New System.Drawing.Size(300, 300)
        ' venstertitel
        Me.Text = "Form1"
    End Sub

    Shared Sub Main()
        ' de app wordt gestart
        Application.Run(New Form1)
    End Sub
End Class

5.2.7. Conclusie

We nemen nu de door VS.NET gegenereerde code zoals deze is en voegen er alleen onze eigen code aan toe, met name om de gebeurtenissen te beheren die verband houden met de verschillende controlevelden van het formulier.

5.3. Venster met invoerveld, knop en tekstlabel

5.3.1. Grafisch ontwerp

In het vorige voorbeeld hadden we nog geen componenten in het venster geplaatst. We beginnen een nieuw project met de naam interface2. Hiervoor volgen we de eerder beschreven procedure om een project aan te maken:

Image

Laten we nu een venster maken met een knop, een label en een invoerveld:

De velden zijn als volgt:

nr.
naam
type
rol
1
lblSaisie
Label
een omschrijving
2
txtSaisie
TextBox
een invoerveld
3
btnAfficher
Knop
om de inhoud van het invoerveld txtSaisie in een dialoogvenster weer te geven

U kunt als volgt te werk gaan om dit venster te maken: klik met de rechtermuisknop in het venster buiten een component en kies de optie Properties om toegang te krijgen tot de eigenschappen van het venster:

Image

Het eigenschappenvenster verschijnt dan aan de rechterkant:

Image

Enkele van deze eigenschappen zijn het vermelden waard:

BackColor
om de achtergrondkleur van het venster in te stellen
ForeColor
om de kleur van de afbeeldingen of tekst in het venster in te stellen
Menu
om een menu aan het venster te koppelen
Text
om het venster een titel te geven
FormBorderStyle
om het type venster vast te stellen
Font
om het lettertype van de tekst in het venster in te stellen
Name
om de naam van het venster vast te leggen

Hier stellen we de eigenschappen Text en Name in:

Text
Invoervelden & knoppen - 1
Name
frmSaisiesBoutons

Met behulp van de balk "Werkgereedschap"

  • selecteer je de componenten die je nodig hebt
  • plaats ze in het venster en geef ze de juiste afmetingen
Zodra u het onderdeel in de "toolbox" hebt gekozen, gebruikt u de toets
"Esc" om de werkbalk te verbergen, en plaats vervolgens
en pas de afmetingen van de component aan. Doe dit voor de drie
: Label, TextBox, Button. Om de componenten uit te lijnen en
de componenten op de juiste manier te dimensioneren, gebruik je het menu Format:
Het principe van de opmaak is als volgt:
  1. selecteer de verschillende componenten die je samen wilt opmaken (houd de Ctrl-toets ingedrukt)
  2. selecteer het gewenste opmaaktype
Met de optie Align kunt u componenten uitlijnen
Met de optie Make Same Size kunt u ervoor zorgen dat
componenten dezelfde hoogte of dezelfde
breedte krijgen:
Met de optie 'Horizontal Spacing' kun je bijvoorbeeld
componenten horizontaal uit te lijnen met
afstanden van gelijke grootte. Hetzelfde geldt
geldt voor de optie ‘Vertical Spacing’ om componenten verticaal uit te lijnen.
Met de optie 'Center in Form' kun je
een component horizontaal of
verticaal in het venster te centreren:
Zodra de componenten correct
in het venster zijn geplaatst, stelt u hun eigenschappen in.
Klik hiervoor met de rechtermuisknop op de component en
kies de optie Properties:
  • label 1 (Label)
Selecteer de component om
het eigenschappenvenster te openen. Wijzig daarin
de volgende eigenschappen aan:
naam: lblSaisie, tekst: Invoer
  • het invoerveld 2 (TextBox)
Selecteer de component om het
eigenschappenvenster te openen. Wijzig daarin
de volgende eigenschappen:
naam: txtSaisie, tekst: laat leeg
  • knop 3 (Button):
name: cmdAfficher, text: Weergeven
  • het venster zelf: name: frmSaisies&Knoppen,
text: Invoervelden & knoppen - 1
We kunnen ons project uitvoeren (Ctrl-F5)
om een eerste indruk te krijgen van het venster
in actie:

Sluit het venster. Nu moeten we nog de procedure schrijven die wordt geactiveerd wanneer op de knop wordt geklikt: Afficher.

5.3.2. Beheer van gebeurtenissen in een formulier

Laten we eens kijken naar de code die door de grafische ontwerper is gegenereerd:


...

Public Class frmSaisiesBoutons
  Inherits System.Windows.Forms.Form

  ' componenten
    Private components As System.ComponentModel.Container = Nothing
    Friend WithEvents btnAfficher As System.Windows.Forms.Button
    Friend WithEvents lblsaisie As System.Windows.Forms.Label
    Friend WithEvents txtsaisie As System.Windows.Forms.TextBox

  ' constructor
  Public Sub New()
    InitializeComponent()
  End Sub

...

  ' initialisatie van de componenten
    Private Sub InitializeComponent()
...
    End Sub
End Class

Allereerst valt de specifieke declaratie van de componenten op:

  • het sleutelwoord `Friend` geeft aan dat de component zichtbaar is voor alle klassen in het project
  • het sleutelwoord WithEvents geeft aan dat de component gebeurtenissen genereert. We gaan nu kijken hoe we deze gebeurtenissen kunnen beheren

Open het codevenster van het formulier (Weergave/code of F7):

Het bovenstaande venster bevat twee vervolgkeuzelijsten (1) en (2). Lijst (1) is de lijst met formuliercomponenten:

Image

Lijst (2) is de lijst met gebeurtenissen die gekoppeld zijn aan de in (1) geselecteerde component:

Image

Een van de gebeurtenissen die aan de component is gekoppeld, wordt vetgedrukt weergegeven (hier: Click). Dit is de standaardgebeurtenis van de component. U kunt de handler voor deze specifieke gebeurtenis openen door te dubbelklikken op de component in het ontwerpvenster. VB.net genereert dan automatisch het skelet van de gebeurtenishandler in het codevenster en plaatst de gebruiker daarop. De handlers van de andere gebeurtenissen zijn te openen in het codevenster door de component in de lijst (1) en de gebeurtenis in (2) te selecteren. VB.net genereert dan het skelet van de gebeurtenishandler of plaatst de cursor erop als deze al was gegenereerd:


    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

Standaard noemt VB.net C_E de gebeurtenishandler E van component C. Deze naam kan naar wens worden gewijzigd. Dit wordt echter afgeraden. Ontwikkelaars van VB behouden doorgaans de door VB gegenereerde naam, wat zorgt voor een consistente naamgeving in alle VB-programma's. De koppeling van de procedure btnAfficher_Click aan de Click-gebeurtenis van de component btnAfficher gebeurt niet via de naam van de procedure, maar via het sleutelwoord handles:


Handles btnAfficher.Click

Hierboven geeft het sleutelwoord **handles aan dat de procedure de Click-gebeurtenis van de component btnAfficher** afhandelt. De voorgaande gebeurtenishandler heeft twee parameters:

sender
het object dat de gebeurtenis veroorzaakt (in dit geval de knop)
e
een object EventArgs dat de details van de gebeurtenis bevat

We zullen hier geen van deze parameters gebruiken. We hoeven nu alleen nog maar de code aan te vullen. Hier willen we een dialoogvenster weergeven met daarin de inhoud van het veld txtSaisie:


    Private Sub btnAfficher_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' de tekst weergeven die in het invoerveld is ingevoerd TxtSaisie
        MessageBox.Show("texte saisi= " + txtsaisie.Text, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

Als we de applicatie uitvoeren, krijgen we het volgende te zien:

Image

5.3.3. Een andere methode om gebeurtenissen te beheren

Voor de knop btnAfficher heeft VS.NET de volgende code gegenereerd:


Private Sub InitializeComponent()
        ...
        '
        'btnAfficher
        '
        Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
        Me.btnAfficher.Name = "btnAfficher"
        Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
        Me.btnAfficher.TabIndex = 2
        Me.btnAfficher.Text = "Afficher"
...
    End Sub

...

    ' behandeling van een eventuele klik op de knop cmdAfficher
    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

We kunnen de procedure btnAfficher_Click op een andere manier koppelen aan de Click-gebeurtenis van de knop btnAfficher:


Private Sub InitializeComponent()
        ...
        '
        'btnAfficher
        '
        Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
        Me.btnAfficher.Name = "btnAfficher"
        Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
        Me.btnAfficher.TabIndex = 2
        Me.btnAfficher.Text = "Afficher"
        AddHandler btnAfficher.Click, AddressOf btnAfficher_Click
...
    End Sub

...

    ' gebeurtenisbeheerder bij klik op knop cmdAfficher
    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
    End Sub

De procedure btnAfficher_Click heeft het trefwoord Handles verloren en daarmee ook de koppeling met de gebeurtenis btnAfficher.Click. Deze koppeling wordt nu gemaakt met behulp van het trefwoord AddHandler:


        AddHandler btnAfficher.Click, AddressOf btnAfficher_Click

De bovenstaande code, die in de procedure InitializeComponent van het formulier wordt geplaatst, koppelt de procedure met de naam btnAfficher_Click aan de gebeurtenis btnAfficher.Click. Bovendien heeft de component btnAfficher het trefwoord WithEvents niet meer nodig:


    Friend btnAfficher As System.Windows.Forms.Button

Wat is het verschil tussen beide methoden?

  • Met het sleutelwoord `handles` kan een gebeurtenis alleen tijdens het ontwerpen aan een procedure worden gekoppeld. De ontwerper weet van tevoren dat een procedure P de gebeurtenissen E1, E2, ... moet afhandelen en schrijft de code

    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) handles E1, E2, ..., En

Het is inderdaad mogelijk dat een procedure meerdere gebeurtenissen afhandelt.

  • Met het sleutelwoord `addhandler` kan tijdens de uitvoering een gebeurtenis aan een procedure worden gekoppeld. Dit is nuttig in een ‘producent-consument’-context voor gebeurtenissen. Een object genereert een specifieke gebeurtenis die voor andere objecten van belang kan zijn. Deze objecten abonneren zich bij de producent om de gebeurtenis te ontvangen (bijvoorbeeld een temperatuur die een kritieke drempel heeft overschreden). Tijdens de uitvoering van de applicatie zal de producent van de gebeurtenis verschillende instructies moeten uitvoeren:
Addhandler E, AddressOf P1
Addhandler E, AddressOf P2
...
Addhandler E, AddressOf Pn

waarbij E de door de producent geproduceerde gebeurtenis is en Pi de procedures die behoren tot de verschillende objecten die deze gebeurtenis verbruiken. In een volgend hoofdstuk zullen we nog terugkomen op een producent-consument-toepassing voor gebeurtenissen.

5.3.4. Conclusie

Uit de twee bestudeerde projecten kunnen we concluderen dat, zodra de grafische interface is gebouwd met VS.NET, het werk van de ontwikkelaar bestaat uit het schrijven van de gebeurtenishandlers die hij voor deze grafische interface wil beheren. We zullen voortaan alleen nog de code hiervan presenteren.

5.4. Enkele nuttige componenten

We presenteren nu diverse applicaties waarin de meest gangbare componenten worden gebruikt, om de belangrijkste methoden en eigenschappen ervan te ontdekken. Voor elke applicatie presenteren we de grafische interface en de relevante code, met name de gebeurtenishandlers.

5.4.1. Formulier

We beginnen met de onmisbare component: het formulier waarop componenten worden geplaatst. We hebben al enkele basis-eigenschappen ervan besproken. Hier gaan we dieper in op enkele belangrijke gebeurtenissen van een formulier.

 
het formulier wordt geladen
Closing
het formulier wordt gesloten
Closed
het formulier is gesloten

De gebeurtenis Load vindt plaats nog voordat het formulier wordt weergegeven. De gebeurtenis Closing vindt plaats wanneer het formulier wordt gesloten. Dit sluiten kan nog programmatisch worden tegengehouden. We maken een formulier met de naam Form1 zonder componenten:

Image

We verwerken de drie voorgaande gebeurtenissen:


    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' het formulier wordt voor het eerst geladen
        MessageBox.Show("Evt Load", "Load")
    End Sub

    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        ' het formulier wordt gesloten
        MessageBox.Show("Evt Closing", "Closing")
        ' er wordt om bevestiging gevraagd
        Dim réponse As DialogResult = MessageBox.Show("Voulez-vous vraiment quitter l'application", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
        If réponse = DialogResult.No Then
            e.Cancel = True
        End If
    End Sub

    Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
        ' het formulier wordt gesloten
        MessageBox.Show("Evt Closed", "Closed")
    End Sub
We gebruiken de functie MessageBox om op de hoogte te worden gesteld van de verschillende
gebeurtenissen. De gebeurtenis Closing vindt plaats wanneer de gebruiker
het venster sluit.
We vragen hem dan of hij de applicatie echt wil afsluiten:
Als hij ‘Nee’ antwoordt, stellen we de eigenschap Cancel van de gebeurtenis
CancelEventArgs die de methode als parameter heeft ontvangen.
Als we deze eigenschap instellen op False, wordt het sluiten
van het venster wordt afgebroken, anders gaat het door:

5.4.2. Label- en invoervakken TextBox

We zijn deze twee componenten al tegengekomen. Label is een tekstcomponent en TextBox een invoerveldcomponent. Hun belangrijkste eigenschap is Text, die ofwel de inhoud van het invoerveld ofwel de tekst van het label aangeeft. Deze eigenschap is lees- en schrijfbaar. De gebeurtenis die doorgaans voor TextBox wordt gebruikt, is TextChanged, die aangeeft dat de gebruiker het invoerveld heeft gewijzigd. Hier volgt een voorbeeld waarin de gebeurtenis TextChanged wordt gebruikt om de wijzigingen in een invoerveld bij te houden:

nr.
type
naam
rol
1
TextBox
txtSaisie
invoerveld
2
Label
lblControle
geeft de tekst van 1 in realtime weer
3
Knop
cmdEffacer
om de velden 1 en 2 te wissen
4
Knop
cmdQuitter
om de applicatie te verlaten

De relevante code voor deze applicatie is die van de gebeurtenishandlers:


  ' klik op de knop 'Afsluiten'
    Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdQuitter.Click
        ' klik op de knop 'Afsluiten' – de applicatie wordt afgesloten
        Application.Exit()
    End Sub

    ' wijziging in veld txtSaisie
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' de inhoud van TextBox is gewijzigd – deze wordt gekopieerd naar het label lblControle
        lblControle.Text = txtSaisie.Text
    End Sub

    ' klik op de knop 'Wissen'
    Private Sub cmdEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdEffacer.Click
        ' de inhoud van het invoerveld wordt gewist
        txtSaisie.Text = ""
    End Sub

    ' er is een toets ingedrukt
    Private Sub txtSaisie_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
    Handles txtSaisie.KeyPress
        ' er wordt gekeken welke toets is ingedrukt
        Dim touche As Char = e.KeyChar
        If touche = ControlChars.Cr Then
            MessageBox.Show(txtSaisie.Text, "Contrôle", MessageBoxButtons.OK, MessageBoxIcon.Information)
            e.Handled = True
        End If
    End Sub

Let op de manier waarop de applicatie wordt afgesloten in de procedure cmdQuitter_Click: Application.Exit(). Het volgende voorbeeld maakt gebruik van een TextBox met meerdere regels:

De lijst met besturingselementen is als volgt:

nr.
type
naam
rol
1
TextBox
txtMultiLignes
meerregelig invoerveld
2
TextBox
txtAjout
invoerveld met één regel
3
Knop
btnAjouter
Voegt de inhoud van 2 naar 1 toe

Om een TextBox om te zetten naar een meerregelig veld, stelt u de volgende eigenschappen van het besturingselement in:

 
om meerdere regels tekst toe te staan
ScrollBars=( None, Horizontal, Vertical, Both)
om aan te geven of het besturingselement scrollbalken moet hebben (Horizontal, Vertical, Both) of niet (None)
AcceptReturn=(True, False)
indien gelijk aan true, zorgt de Enter-toets ervoor dat er een nieuwe regel wordt ingevoegd
AcceptTab=(True, False)
indien gelijk aan true, zal de Tab-toets een tabstop in de tekst genereren

De relevante code is die welke de klik op de knop [Ajouter] verwerkt en die welke de wijziging van het invoerveld [txtAjout] verwerkt:


    ' eventueel btnAjouter_Click
    Private Sub btnAjouter_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' de inhoud van txtAjout wordt toegevoegd aan die van txtMultilignes
        txtMultilignes.Text &= txtAjout.Text
        txtAjout.Text = ""
    End Sub

    ' evt txtAjout_TextChanged
    Private Sub txtAjout_TextChanged1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtAjout.TextChanged
        ' de status van de knop ‘Toevoegen’ wordt vastgelegd
        btnAjouter.Enabled = txtAjout.Text.Trim() <> ""
    End Sub

5.4.3. keuzelijsten ComboBox

Een ComboBox-component is een keuzelijst in combinatie met een invoerveld: de gebruiker kan ofwel een item selecteren in (2) of tekst invoeren in (1). Er zijn drie soorten ComboBox, bepaald door de eigenschap Style:

 
niet-uitklapbare lijst met bewerkingsveld
DropDown
uitklapbare lijst met invoerveld
DropDownList
uitklaplijst zonder invoerveld

Standaard is het type van een ComboBox DropDown. Om de klasse ComboBox te bekijken, typ je ComboBox in de helpindex (Aide/Index). De klasse ComboBox heeft één constructor:

Public Sub New()
maakt een leeg ComboBox-object aan

De elementen van ComboBox zijn beschikbaar in de eigenschap Items:

Public ReadOnly Property Items As ComboBox.ObjectCollection

Dit is een geïndexeerde eigenschap, waarbij Items(i) verwijst naar het i-de element van de combobox. Stel dat C een combobox is en C.Items de lijst met elementen. We hebben de volgende eigenschappen:

 
aantal elementen van de combobox
C.Items(i)
element i van de combobox
C.Add(object o)
voegt het object o toe als laatste element van de combobox
C.AddRange(object() objets)
voegt een array met objecten toe aan het einde van de combo
C.Insert(int i, object o)
voegt het object o toe op positie i van de combobox
C.RemoveAt(int i)
verwijdert element i uit de combobox
C.Remove(object o)
verwijdert het object o uit de combobox
C.Clear()
verwijdert alle elementen uit de combo
C.IndexOf(object o)
geeft de positie i van het object o in de keuzelijst weer

Het is misschien verrassend dat een keuzelijst objecten kan bevatten, terwijl deze gewoonlijk tekenreeksen bevat. Visueel gezien is dit inderdaad het geval. Als een ComboBox een object obj bevat, wordt de tekenreeks obj.ToString weergegeven(). We herinneren ons dat elk object een methode ToString heeft die is geërfd van de klasse object en die een tekenreeks retourneert die ‘representatief’ is voor het object. Het geselecteerde element in de keuzelijst C is C.SelectedItem of C.Items(C.SelectedIndex), waarbij C.SelectedIndex het nummer is van het geselecteerde element, dit nummer begint bij nul voor het eerste element.

Bij het selecteren van een item in de keuzelijst vindt de gebeurtenis SelectedIndexChanged plaats, die vervolgens kan worden gebruikt om een melding te krijgen over de wijziging in de selectie in de keuzelijst. In de volgende toepassing gebruiken we deze gebeurtenis om het item weer te geven dat in de lijst is geselecteerd.

Image

We tonen alleen de relevante code van het venster. In de formulierconstructor vullen we de keuzelijst in:


  Public Sub New()
    ' formulier aanmaken
    InitializeComponent()
    ' combo invullen
    cmbNombres.Items.AddRange(New String() {"zéro", "un", "deux", "trois", "quatre"})
        ' we selecteren het eerste element uit de lijst
    cmbNombres.SelectedIndex = 0
  End Sub

We verwerken de gebeurtenis SelectedIndexChanged van de keuzelijst die aangeeft dat er een nieuw item is geselecteerd:


    Private Sub cmbNombres_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmbNombres.SelectedIndexChanged
        ' het geselecteerde element is gewijzigd – we geven het weer
        MessageBox.Show("Elément sélectionné : (" & cmbNombres.SelectedItem.ToString & "," & cmbNombres.SelectedIndex & ")", "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

5.4.4. component ListBox

We stellen voor om de volgende interface te bouwen:

De componenten van dit venster zijn de volgende:

nr.
type
naam
rol/eigenschappen
0
Form
Form1
formulier - BorderStyle=FixedSingle
1
TextBox
txtSaisie
invoerveld
2
Knop
btnAjouter
knop waarmee de inhoud van invoerveld 1 aan lijst 3 kan worden toegevoegd
3
ListBox
listBox1
lijst 1
4
ListBox
listBox2
lijst 2
5
Knop
btn1TO2
verplaatst de geselecteerde items van lijst 1 naar lijst 2
6
Knop
cmd2T0
doet het omgekeerde
7
Knop
btnEffacer1
de lijst leegmaken 1
8
Knop
btnEffacer2
lijst 2 leegmaken
  • De gebruiker typt tekst in veld 1. Hij voegt deze toe aan lijst 1 met de knop Ajouter (2). Het invoerveld (1) wordt vervolgens leeggemaakt en de gebruiker kan een nieuw item toevoegen.
  • Hij kan items van de ene lijst naar de andere verplaatsen door het te verplaatsen item in een van de lijsten te selecteren en de juiste verplaatsknop 5 of 6 te kiezen. Het verplaatste item wordt aan het einde van de doellijst toegevoegd en uit de bronlijst verwijderd.
  • Hij kan dubbelklikken op een item in lijst 1. Dit item wordt dan naar het invoerveld verplaatst om te worden bewerkt en uit lijst 1 verwijderd.

De knoppen zijn ingeschakeld of uitgeschakeld volgens de volgende regels:

  • de knop Ajouter is alleen actief als er tekst in het invoerveld staat
  • de knop 5 voor het overzetten van lijst 1 naar lijst 2 is alleen actief als er een item is geselecteerd in lijst 1
  • de knop 6 voor het overzetten van lijst 2 naar lijst 1 is alleen actief als er een item is geselecteerd in lijst 2
  • de knoppen 7 en 8 voor het wissen van lijst 1 en lijst 2 zijn alleen verlicht als de te wissen lijst items bevat.

Onder de bovenstaande voorwaarden moeten alle knoppen bij het opstarten van de applicatie uitgeschakeld zijn. De eigenschap Enabled van de knoppen moet dan worden ingesteld op false. Dit kan tijdens het ontwerpen worden gedaan, waardoor de bijbehorende code in de methode InitializeComponent wordt gegenereerd, of we kunnen dit zelf in de constructor doen, zoals hieronder:


    Public Sub New()
        ' het formulier wordt voor het eerst aangemaakt
        InitializeComponent()
        ' aanvullende initialisaties
        ' een aantal knoppen worden uitgeschakeld
        btnAjouter.Enabled = False
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
        btnEffacer1.Enabled = False
        btnEffacer2.Enabled = False
    End Sub

De status van de knop Ajouter wordt bepaald door de inhoud van het invoerveld. Met de gebeurtenis TextChanged kunnen we de wijzigingen in deze inhoud volgen:


    ' wijziging in tekstinvoerveld
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' de inhoud van txtSaisie is gewijzigd
        ' de knop Toevoegen is alleen actief als het veld niet leeg is
        btnAjouter.Enabled = txtSaisie.Text.Trim() <> ""
    End Sub

De status van de overdrachtsknoppen hangt af van het feit of er al dan niet een item is geselecteerd in de lijst die ze beheren:


    ' wijziging van het geselecteerde element zonder listbox1
    Private Sub listBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox1.SelectedIndexChanged

        ' er is een element geselecteerd
        ' de knop voor overdracht van 1 naar 2 wordt geactiveerd
        btn1TO2.Enabled = True
    End Sub

    ' wijziging van het geselecteerde element zonder listbox2
    Private Sub listBox2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox2.SelectedIndexChanged
        ' er is een element geselecteerd
        ' de knop voor overdracht van 2 naar 1 wordt geactiveerd
        btn2TO1.Enabled = True
    End Sub

De code die hoort bij het klikken op de knop Ajouter is als volgt:


    ' klik op de knop Toevoegen
    Private Sub btnAjouter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' een nieuw element toegevoegd aan lijst 1
        listBox1.Items.Add(txtSaisie.Text.Trim())
        ' de invoer wordt gewist
        txtSaisie.Text = ""
        ' Lijst 1 is niet leeg
        btnEffacer1.Enabled = True
        ' focus terug naar het invoerveld
        txtSaisie.Focus()
    End Sub

Let op de methode Focus, waarmee de „focus” op een formulierveld kan worden geplaatst. De code die hoort bij het klikken op de knoppen Effacer:


    ' klik op knop Wissen1
    Private Sub btnEffacer1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer1.Click
        ' lijst 1 wordt gewist
        listBox1.Items.Clear()
        btnEffacer1.Enabled = False
    End Sub

    ' klik op knop Wissen2
    Private Sub btnEffacer2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' lijst 2 wordt gewist
        listBox2.Items.Clear()
        btnEffacer2.Enabled = False
    End Sub

De code om de geselecteerde elementen van de ene lijst naar de andere over te zetten:


    ' klik op knop knop1naar2
    Private Sub btn1TO2_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn1TO2.Click
        ' het geselecteerde item wordt overgebracht van lijst 1 naar lijst 2
        transfert(listBox1, listBox2)
        ' Knoppen 'Wissen'
        btnEffacer2.Enabled = True
        btnEffacer1.Enabled = listBox1.Items.Count <> 0
        ' overzetknoppen
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' klik op knop btn2to1
    Private Sub btn2TO1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn2TO1.Click
        ' het geselecteerde item verplaatsen van Lijst 2 naar Lijst 1
        transfert(listBox2, listBox1)
        ' knoppen Wissen
        btnEffacer1.Enabled = True
        btnEffacer2.Enabled = listBox2.Items.Count <> 0
        ' overzetknoppen
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' overzetten
    Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
        ' het geselecteerde item overbrengen van lijst 1 naar lijst l2
        ' een geselecteerd element?
        If l1.SelectedIndex = -1 Then Return
        ' toevoegen aan l2
        l2.Items.Add(l1.SelectedItem)
        ' verwijderen uit l1
        l1.Items.RemoveAt(l1.SelectedIndex)
    End Sub

Allereerst maken we een methode aan


    Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)

die het geselecteerde element uit de lijst l1 overbrengt naar de lijst l2. Zo hebben we slechts één methode nodig in plaats van twee om een element van listBox1 naar listBox2 of van listBox2 naar listBox1 over te zetten. Voordat we de overdracht uitvoeren, controleren we of er daadwerkelijk een element is geselecteerd in de lijst l1:


        ' een geselecteerd element?
        If l1.SelectedIndex = -1 Then Return

De eigenschap SelectedIndex heeft de waarde -1 als er momenteel geen item is geselecteerd. In de procedures


    Private Sub btnXTOY_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnXTOY.Click

wordt de lijst X overgezet naar lijst Y en wordt de status van bepaalde knoppen aangepast om de nieuwe status van de lijsten weer te geven.

5.4.5. selectievakjes CheckBox, keuzerondjes ButtonRadio

We stellen voor om de volgende applicatie te schrijven:

De componenten van het venster zijn als volgt:

nr.
type
naam
rol
1
RadioButton
radioButton1
radioButton2
radioButton3
3 keuzerondjes
2
CheckBox
chechBox1
chechBox2
chechBox3
3 selectievakjes
3
ListBox
lstValeurs
een lijst

Als we de drie keuzerondjes achter elkaar plaatsen, maken ze standaard deel uit van dezelfde groep. Wanneer er dus één is aangevinkt, zijn de andere dat niet. De gebeurtenis die voor deze zes besturingselementen van belang is, is de gebeurtenis CheckChanged, die aangeeft dat de status van het selectievakje of het keuzerondje is gewijzigd. Deze status wordt in beide gevallen weergegeven door de booleaanse eigenschap Check, die, indien waar, aangeeft dat het besturingselement is aangevinkt. We hebben hier één enkele methode gebruikt om de zes gebeurtenissen CheckChanged te verwerken, namelijk de methode affiche:


  ' weergeeft
    Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles checkBox1.CheckedChanged, checkBox2.CheckedChanged, checkBox3.CheckedChanged, _
    radioButton1.CheckedChanged, radioButton2.CheckedChanged, radioButton3.CheckedChanged
        ' geeft de status van de keuzeknop of het selectievakje weer
        ' is dit een selectievakje?
        If (TypeOf (sender) Is CheckBox) Then
            Dim chk As CheckBox = CType(sender, CheckBox)
            lstValeurs.Items.Insert(0, chk.Name & "=" & chk.Checked)
        End If
        ' is dit een keuzerondje?
        If (TypeOf (sender) Is RadioButton) Then
            Dim rdb As RadioButton = CType(sender, RadioButton)
            lstValeurs.Items.Insert(0, rdb.Name & "=" & rdb.Checked)
        End If
    End Sub

Met de syntaxis TypeOf (sender) Is CheckBox kan worden gecontroleerd of het object sender van het type CheckBox is. Hierdoor kunnen we vervolgens een typeconversie uitvoeren naar het exacte type van sender. De methode affiche schrijft in de lijst lstValeurs de naam van de component die de gebeurtenis heeft veroorzaakt en de waarde van de eigenschap Checked. Tijdens de uitvoering zien we dat een klik op een keuzeknop twee gebeurtenissen CheckChanged veroorzaakt: één op de oude aangevinkte knop, die nu „niet aangevinkt” wordt, en één op de nieuwe knop, die nu „aangevinkt” wordt.

5.4.6. schakelaars ScrollBar

Er zijn verschillende soorten variatoren: de horizontale variator (hScrollBar),
de verticale variator (vScrollBar) en de incrementeerder (NumericUpDown).

Laten we de volgende toepassing maken:

nr.
type
naam
rol
1
hScrollBar
hScrollBar1
een horizontale variator
2
hScrollBar
hScrollBar2
een horizontale variator die de variaties van variator 1 volgt
3
TextBox
txtValeur
geeft de waarde van de horizontale regelaar weer
ReadOnly=true om invoer te voorkomen
4
NumericUpDown
incrementator
maakt het mogelijk de waarde van regelaar 2 vast te zetten
  • Met een schuifregelaar van het type ScrollBar kan de gebruiker een waarde kiezen binnen een bereik van gehele getallen, gesymboliseerd door de "strook" van de schuifregelaar waarover een cursor beweegt. De waarde van de schuifregelaar is beschikbaar in de eigenschap Value.
  • Bij een horizontale schuifregelaar vertegenwoordigt het linkeruiteinde de minimumwaarde van het bereik, het rechteruiteinde de maximumwaarde en de schuifknop de huidige geselecteerde waarde. Bij een verticale schuifregelaar wordt het minimum vertegenwoordigd door het bovenste uiteinde en het maximum door het onderste uiteinde. Deze waarden worden weergegeven door de eigenschappen Minimum en Maximum en zijn standaard ingesteld op 0 en 100.
  • Een klik op de uiteinden van de schuifregelaar verandert de waarde met één stap (positief of negatief), afhankelijk van het aangeklikte uiteinde, genaamd SmallChange, dat standaard 1 is.
  • Een klik aan weerszijden van de schuifregelaar verandert de waarde met één stap (positief of negatief), afhankelijk van het aangeklikte uiteinde, genaamd LargeChange, dat standaard 10 is.
  • Wanneer je op het bovenste uiteinde van een verticale schuifbalk klikt, neemt de waarde af. Dit kan de gemiddelde gebruiker verrassen, die normaal gesproken verwacht dat de waarde „stijgt“. Dit probleem los je op door de eigenschappen SmallChange en LargeChange een negatieve waarde te geven.
  • Deze vijf eigenschappen (Value, Minimum, Maximum, SmallChange, LargeChange) zijn zowel lees- als schrijfbaar.
  • De belangrijkste gebeurtenis van de regelaar is die welke een waardeverandering signaleert: de Scroll-gebeurtenis.

Een component NumericUpDown bevindt zich in de buurt van de regelaar: ook deze heeft de eigenschappen Minimum, Maximum en Value, standaard ingesteld op 0, 100, 0. Maar hier wordt de eigenschap Value weergegeven in een invoerveld dat integraal deel uitmaakt van het besturingselement. De gebruiker kan deze waarde zelf wijzigen, tenzij de eigenschap ReadOnly van het besturingselement op ‘waar’ is ingesteld. De waarde van de stapgrootte wordt bepaald door de eigenschap Increment, standaard 1. De belangrijkste gebeurtenis van de component NumericUpDown is degene die een waardeverandering signaleert: de gebeurtenis ValueChanged. De relevante code van onze toepassing is als volgt:

Het formulier wordt tijdens de opbouw opgemaakt:


    ' constructor
    Public Sub New()
        ' eerste aanmaak van het formulier
        InitializeComponent()
        ' we geven regelaar 2 dezelfde kenmerken als regelaar 1
        hScrollBar2.Minimum = hScrollBar1.Value
        hScrollBar2.Minimum = hScrollBar1.Minimum
        hScrollBar2.Maximum = hScrollBar1.Maximum
        hScrollBar2.LargeChange = hScrollBar1.LargeChange
        hScrollBar2.SmallChange = hScrollBar1.SmallChange
        ' idem voor de incrementator
        incrémenteur.Minimum = hScrollBar1.Value
        incrémenteur.Minimum = hScrollBar1.Minimum
        incrémenteur.Maximum = hScrollBar1.Maximum
        incrémenteur.Increment = hScrollBar1.SmallChange
        ' aan TextBox wordt de waarde van regelaar 1 toegewezen
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

De handler die de waardeveranderingen van regelaar 1 bijhoudt:


    ' beheer van regelaar hscrollbar1
    Private Sub hScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar1.Scroll
        ' wijziging van de waarde van regelaar 1
        ' de waarde wordt doorgegeven aan regelaar 2 en aan het tekstvak TxtValeur
        hScrollBar2.Value = hScrollBar1.Value
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

De handler die de waardeveranderingen van regelaar 2 volgt:


    ' beheer van de schuifbalk hscrollbar2
    Private Sub hScrollBar2_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar2.Scroll
        ' elke wijziging van regelaar 2 wordt geblokkeerd
        ' door deze te dwingen de waarde van variator 1 te behouden
        e.NewValue = hScrollBar1.Value
    End Sub

De manager die de veranderingen in de besturing incrémenteur bijhoudt:


    ' beheer van de incrementator
    Private Sub incrémenteur_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles incrémenteur.ValueChanged
        ' de waarde van variator 2 wordt vastgezet
        hScrollBar2.Value = CType(incrémenteur.Value, Integer)
    End Sub

5.5. Muisgebeurtenissen

Wanneer je in een container tekent, is het belangrijk om de positie van de muis te kennen, bijvoorbeeld om bij een klik een punt weer te geven. De bewegingen van de muis veroorzaken gebeurtenissen in de container waarin de muis zich verplaatst.

 
de muis is zojuist het gebied van het besturingselement binnengekomen
MouseLeave
de muis heeft zojuist het besturingsgebied verlaten
MouseMove
de muis beweegt binnen het besturingsgebied
MouseDown
De linkermuisknop is ingedrukt
MouseUp
De linkermuisknop loslaten
DragDrop
de gebruiker laat een object op het besturingselement los
DragEnter
de gebruiker komt in het gebied van het besturingselement door een object te slepen
DragLeave
de gebruiker verlaat het gebied van het besturingselement door een object te slepen
DragOver
de gebruiker beweegt met een object over het besturingsgebied

Hier is een programma waarmee je beter kunt begrijpen wanneer de verschillende muisgebeurtenissen plaatsvinden:

nr.
type
naam
rol
1
Label
lblPosition
om de positie van de muis in formulier 1, lijst 2 of knop 3 weer te geven
2
ListBox
lstEvts
om andere muisgebeurtenissen weer te geven dan MouseMove
3
Knop
btnEffacer
om de inhoud van 2 te wissen

De gebeurtenishandlers zijn als volgt. Om de muisbewegingen op de drie besturingselementen te volgen, schrijven we slechts één handler:


    ' evt. form1_mousemove
    Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseMove, lstEvts.MouseMove, btnEffacer.MouseMove, lstEvts.MouseMove
        ' muisbeweging – de coördinaten (X,Y) van de muis worden weergegeven
        lblPosition.Text = "(" & e.X & "," & e.Y & ")"
    End Sub

Het is belangrijk om te weten dat telkens wanneer de muis het gebied van een besturingselement binnenkomt, het coördinatensysteem verandert. Het beginpunt (0,0) is de linkerbovenhoek van het besturingselement waarop de muis zich bevindt. Tijdens de uitvoering, wanneer de muis van het formulier naar de knop wordt verplaatst, is de verandering in coördinaten dus duidelijk zichtbaar. Om deze veranderingen in het muisgebied beter te kunnen zien, kun je de eigenschap Cursor van de besturingselementen gebruiken:

Image

Met deze eigenschap kun je de vorm van de muiscursor instellen wanneer deze het bereik van het besturingselement binnenkomt. In ons voorbeeld hebben we de cursor ingesteld op Default voor het formulier zelf, op Hand voor lijst 2 en op No voor knop 3, zoals te zien is op de onderstaande schermafbeeldingen.

Image

Image

Image

In de methode [InitializeComponent] is de code die door deze keuzes wordt gegenereerd als volgt:


        Me.lstEvts.Cursor = System.Windows.Forms.Cursors.Hand
        Me.btnEffacer.Cursor = System.Windows.Forms.Cursors.No

Om bovendien de muisbewegingen (in en uit) op lijst 2 te detecteren, verwerken we de gebeurtenissen MouseEnter en MouseLeave uit diezelfde lijst:


    ' event lstEvts_MouseEnter
    Private Sub lstEvts_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles lstEvts.MouseEnter
        affiche("MouseEnter sur liste")
    End Sub

    ' gebeurtenis lstEvts_MouseLeave
    Private Sub lstEvts_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles lstEvts.MouseLeave
        affiche("MouseLeave sur liste")
    End Sub
    
' geeft weer
    Private Sub affiche(ByVal message As String)
        ' het bericht wordt bovenaan de lijst met gebeurtenissen weergegeven
        lstEvts.Items.Insert(0, message)
    End Sub

Image

Om klikken op het formulier te verwerken, verwerken we de gebeurtenissen MouseDown en MouseUp:


    ' gebeurtenis Form1_MouseDown
    Private Sub Form1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseDown
        affiche("MouseDown sur formulaire")
    End Sub

    ' gebeurtenis Form1_MouseUp
    Private Sub Form1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseUp
        affiche("MouseUp sur formulaire")
    End Sub

Image

Tot slot de code voor de klikhandler op de knop Effacer:


    ' gebeurtenis btnEffacer_Click
    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' wist de lijst met gebeurtenissen
        lstEvts.Items.Clear()
    End Sub

5.6. Een venster met een menu maken

Laten we nu eens kijken hoe we een venster met een menu kunnen maken. We gaan het volgende venster maken:

Besturingselement 1 is een alleen-lezen TextBox (ReadOnly=true) met de naam txtStatut. De menustructuur is als volgt:

Menuopties zijn besturingselementen, net als andere visuele componenten, en beschikken over eigenschappen en gebeurtenissen. Bijvoorbeeld de eigenschappenlijst van de menuoptie A1:

Image

In ons voorbeeld worden twee eigenschappen gebruikt:

 
de naam van het menubesturingselement
Text
de tekst van de menuoptie

De eigenschappen van de verschillende menuopties in ons voorbeeld zijn als volgt:

Naam
Tekst
mnuA
opties A
mnuA1
A1
mnuA2
A2
mnuA3
A3
mnuB
opties B
mnuB1
B1
mnuSep1
- (scheidingsteken)
mnuB2
B2
mnuB3
B3
mnuB31
B31
mnuB32
B32

Om een menu aan te maken, selecteert u de component "MainMenu" in de balk "ToolBox":

Image

Je krijgt dan een leeg menu dat in het formulier wordt geplaatst met lege velden met de titel "Type Here". Je hoeft alleen maar de verschillende menuopties in te vullen:

Image

Om een scheidingsteken in te voegen tussen twee opties, zoals hierboven tussen de opties B1 en B2, ga je naar de plaats waar het scheidingsteken in het menu moet komen, klik je met de rechtermuisknop en kies je de optie Insert Separator:

Image

Als je de applicatie start met Ctrl-F5, krijg je een formulier te zien met een menu dat voorlopig nog niets doet. De menuopties worden behandeld als componenten: ze hebben eigenschappen en gebeurtenissen. Selecteer in [fenêtre de code] de component mnuA1 en selecteer vervolgens de bijbehorende gebeurtenissen:

Image

Als hierboven de gebeurtenis Click wordt gegenereerd, genereert VS.NET automatisch de volgende procedure:


Private Sub mnuA1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuA.Click
....
    End Sub

We zouden op deze manier voor alle menuopties te werk kunnen gaan. Hier kan voor alle opties dezelfde procedure worden gebruikt. Daarom hernoemen we de vorige procedure naar affiche en definiëren we de gebeurtenissen die deze procedure afhandelt:


    Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles mnuA1.Click, mnuA2.Click, mnuB1.Click, mnuB2.Click, mnuB31.Click, mnuB32.Click
        ' geeft in TextBox de naam van het geselecteerde submenu weer
        txtStatut.Text = (CType(sender, MenuItem)).Text
    End Sub

Bij deze methode geven we alleen de eigenschap Text van de menuoptie weer bij de bron van de gebeurtenis. De bron van de gebeurtenis sender is van het type object. De menuopties zijn van het type MenuItem, dus moeten we hier een typeconversie uitvoeren van object naar MenuItem. Start de toepassing en selecteer de optie A1 om het volgende bericht te krijgen:

Image

De relevante code van deze applicatie is, naast die van de methode affiche, de code voor het opbouwen van het menu in de formulierbouwer (InitializeComponent):


  Private Sub InitializeComponent()
    Me.mainMenu1 = New System.Windows.Forms.MainMenu
    Me.mnuA = New System.Windows.Forms.MenuItem
    Me.mnuA1 = New System.Windows.Forms.MenuItem
    Me.mnuA2 = New System.Windows.Forms.MenuItem
    Me.mnuA3 = New System.Windows.Forms.MenuItem
    Me.mnuB = New System.Windows.Forms.MenuItem
    Me.mnuB1 = New System.Windows.Forms.MenuItem
    Me.mnuB2 = New System.Windows.Forms.MenuItem
    Me.mnuB3 = New System.Windows.Forms.MenuItem
    Me.mnuB31 = New System.Windows.Forms.MenuItem
    Me.mnuB32 = New System.Windows.Forms.MenuItem
    Me.txtStatut = New System.Windows.Forms.TextBox
    Me.mnuSep1 = New System.Windows.Forms.MenuItem
    Me.SuspendLayout()
    ' 
    ' mainMenu1
    ' 
    Me.mainMenu1.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA, Me.mnuB})

    ' 
    ' mnuA
    ' 
    Me.mnuA.Index = 0
    Me.mnuA.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA1, Me.mnuA2, Me.mnuA3})
    Me.mnuA.Text = "Options A"
    ' 
    ' mnuA1
    ' 
    Me.mnuA1.Index = 0
    Me.mnuA1.Text = "A1"
        ' 
    ' mnuA2
    ' 
    Me.mnuA2.Index = 1
    Me.mnuA2.Text = "A2"
        ' 
    ' mnuA3
    ' 
    Me.mnuA3.Index = 2
    Me.mnuA3.Text = "A3"
        ' 
    ' mnuB
    ' 
    Me.mnuB.Index = 1
    Me.mnuB.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB1, Me.mnuSep1, Me.mnuB2, Me.mnuB3})

    Me.mnuB.Text = "Options B"
    ' 
    ' mnuB1
    ' 
    Me.mnuB1.Index = 0
    Me.mnuB1.Text = "B1"
        ' 
    ' mnuB2
    ' 
    Me.mnuB2.Index = 2
    Me.mnuB2.Text = "B2"

    ' 
    ' mnuB3
    ' 
    Me.mnuB3.Index = 3
    Me.mnuB3.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB31, Me.mnuB32})
    Me.mnuB3.Text = "B3"
        ' 
    ' mnuB31
    ' 
    Me.mnuB31.Index = 0
    Me.mnuB31.Text = "B31"
        ' 
    ' mnuB32
    ' 
    Me.mnuB32.Index = 1
    Me.mnuB32.Text = "B32"
        ' 
    ' txtStatut
    ' 
    Me.txtStatut.Location = New System.Drawing.Point(8, 8)
    Me.txtStatut.Name = "txtStatut"
    Me.txtStatut.ReadOnly = True
    Me.txtStatut.Size = New System.Drawing.Size(112, 20)
    Me.txtStatut.TabIndex = 0
    Me.txtStatut.Text = ""
    ' 
    ' mnuSep1
    ' 
    Me.mnuSep1.Index = 1
    Me.mnuSep1.Text = "-"
    ' 
    ' Form1
    ' 
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.ClientSize = New System.Drawing.Size(136, 42)
    Me.Controls.Add(txtStatut)
    Me.Menu = Me.mainMenu1
    Me.Name = "Form1"
    Me.Text = "Menus"
    Me.ResumeLayout(False)
  End Sub

Let op de instructie die het menu aan het formulier koppelt:


    Me.Menu = Me.mainMenu1

5.7. Niet-visuele componenten

We richten ons nu op een aantal niet-visuele componenten: deze worden gebruikt tijdens het ontwerpen, maar zijn niet zichtbaar tijdens de uitvoering.

5.7.1. Dialoogvensters OpenFileDialog en SaveFileDialog

We gaan de volgende applicatie bouwen:

De besturingselementen zijn als volgt:

Nr.
type
naam
rol
1
TextBox meerdere regels
txtTexte
door de gebruiker ingevoerde tekst of tekst die uit een bestand is geladen
2
Knop
btnSauvegarder
hiermee kan de tekst uit 1 in een tekstbestand worden opgeslagen
3
Knop
btnCharger
hiermee kun je de inhoud van een tekstbestand in 1 laden
4
Knop
btnEffacer
wist de inhoud van 1

Er worden twee niet-visuele besturingselementen gebruikt:

Image

Wanneer ze uit de "ToolBox" worden gehaald en op het formulier worden geplaatst, worden ze in een apart gebied onderaan het formulier geplaatst. De "Dialog"-componenten worden uit de "ToolBox" gehaald:

Image

De code van de knop Effacer is eenvoudig:


    Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' het invoerveld wordt gewist
        txtTexte.Text = ""
    End Sub

De klasse SaveFileDialog is als volgt gedefinieerd:

Image

Deze is afgeleid van meerdere klasseniveaus. Van de vele eigenschappen en methoden zullen we de volgende onthouden:

string Filter
de bestandstypen die worden aangeboden in de vervolgkeuzelijst met bestandstypen in het dialoogvenster
int FilterIndex
het nummer van het bestandstype dat standaard in de bovenstaande lijst wordt aangeboden. Begint bij 0.
String
InitialDirectory
de map die aanvankelijk wordt weergegeven voor het opslaan van het bestand
string FileName
de door de gebruiker opgegeven naam van het back-upbestand
DialogResult.ShowDialog()
methode die het dialoogvenster voor het opslaan weergeeft. Levert een resultaat van het type DialogResult op.

De methode ShowDialog toont een dialoogvenster dat er ongeveer zo uitziet:

1
uitklaplijst samengesteld op basis van de eigenschap Filter. Het standaard voorgestelde bestandstype wordt bepaald door FilterIndex
2
huidige map, vastgelegd door InitialDirectory indien deze eigenschap is ingevuld
3
de bestandsnaam die is gekozen of rechtstreeks door de gebruiker is ingevoerd. Deze is beschikbaar in de eigenschap FileName
4
knoppen Opslaan/Annuleren. Als de knop Enregistrer wordt gebruikt, levert de functie ShowDialog het resultaat DialogResult.OK op

De opslagprocedure kan als volgt worden geschreven:


    Private Sub btnSauvegarder_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnSauvegarder.Click
        ' het invoerveld wordt opgeslagen in een tekstbestand
        ' het dialoogvenster wordt geconfigureerd savefileDialog1
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        saveFileDialog1.FilterIndex = 0
        ' het dialoogvenster wordt weergegeven en het resultaat wordt opgehaald
        If saveFileDialog1.ShowDialog() = DialogResult.OK Then
            ' de bestandsnaam wordt opgehaald
            Dim nomFichier As String = saveFileDialog1.FileName
            Dim fichier As StreamWriter = Nothing
            Try
                ' het bestand wordt geopend in schrijfmodus
                fichier = New StreamWriter(nomFichier)
                ' hier wordt de tekst ingevoerd
                fichier.Write(txtTexte.Text)
            Catch ex As Exception
                ' probleem
                MessageBox.Show("Problème à l'écriture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' het bestand wordt gesloten
                Try
                    fichier.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • We koppelen het oorspronkelijke dossier aan het dossier dat het uitvoerbare bestand van de toepassing bevat:
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • We stellen de weer te geven bestandstypen in
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"

Let op de syntaxis van de filters: filter1|filter2|..|filtren, waarbij filteri = Tekst|bestandssjabloon. Hier heeft de gebruiker de keuze tussen *.txt-bestanden en *.*-bestanden.

  • We stellen het bestandstype vast dat als eerste moet worden weergegeven
        saveFileDialog1.FilterIndex = 0

Hier worden de bestanden van het type *.txt als eerste aan de gebruiker getoond.

  • Het dialoogvenster wordt weergegeven en het resultaat wordt opgehaald

    If saveFileDialog1.ShowDialog() = DialogResult.OK Then
  • Terwijl het dialoogvenster wordt weergegeven, heeft de gebruiker geen toegang meer tot het hoofdformulier (dit wordt een modaal dialoogvenster genoemd). De gebruiker stelt de naam van het op te slaan bestand in en verlaat het dialoogvenster door op de knop Opslaan of Annuleren te klikken, of door het venster te sluiten. Het resultaat van de methode ShowDialog is alleen DialogResult.OK als de gebruiker de knop Enregistrer heeft gebruikt om het dialoogvenster te verlaten.
  • Zodra dit is gebeurd, staat de naam van het aan te maken bestand nu in de eigenschap FileName van het object saveFileDialog1. Vervolgens keert men terug naar het klassieke aanmaken van een tekstbestand. Hierin wordt de inhoud van TextBox: txtTexte.Text geschreven, waarbij eventuele uitzonderingen worden afgehandeld.

De klasse OpenFileDialog lijkt sterk op de klasse SaveFileDialog en is afgeleid van dezelfde reeks klassen. Van deze eigenschappen en methoden onthouden we de volgende:

string Filter
de bestandstypen die worden aangeboden in de vervolgkeuzelijst met bestandstypen in het dialoogvenster
int FilterIndex
het nummer van het bestandstype dat standaard in de bovenstaande lijst wordt aangeboden. Begint bij 0.
string InitialDirectory
de map die in eerste instantie wordt weergegeven voor het zoeken naar het te openen bestand
string FileName
de door de gebruiker opgegeven naam van het te openen bestand
DialogResult.ShowDialog()
methode die het dialoogvenster voor het opslaan weergeeft. Levert een resultaat van het type DialogResult op.

De methode ShowDialog geeft een dialoogvenster weer dat er ongeveer zo uitziet:

1
een vervolgkeuzelijst die is samengesteld op basis van de eigenschap Filter. Het standaard voorgestelde bestandstype wordt bepaald door FilterIndex
2
huidige map, vastgelegd door InitialDirectory indien deze eigenschap is ingevuld
3
de bestandsnaam die is gekozen of rechtstreeks door de gebruiker is ingevoerd. Deze is beschikbaar in de eigenschap FileName
4
knoppen Openen/Annuleren. Als de knop Ouvrir wordt gebruikt, levert de functie ShowDialog het resultaat DialogResult.OK op

De procedure voor het openen kan als volgt worden geschreven:


Private Sub btnCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCharger.Click
        ' een tekstbestand in het invoerveld laden
        ' het dialoogvenster wordt geconfigureerd openfileDialog1
        openFileDialog1.InitialDirectory = Application.ExecutablePath
        openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        openFileDialog1.FilterIndex = 0
        ' het dialoogvenster wordt weergegeven en het resultaat wordt opgehaald
        If openFileDialog1.ShowDialog() = DialogResult.OK Then
            ' de bestandsnaam wordt opgehaald
            Dim nomFichier As String = openFileDialog1.FileName
            Dim fichier As StreamReader = Nothing
            Try
                ' het bestand wordt geopend in leesmodus
                fichier = New StreamReader(nomFichier)
                ' het hele bestand wordt gelezen en opgeslagen in het TextBox
                txtTexte.Text = fichier.ReadToEnd()
            Catch ex As Exception
                ' probleem
                MessageBox.Show("Problème à la lecture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' het bestand wordt gesloten
                Try
                    fichier.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • De startmap wordt ingesteld op de map die het uitvoerbare bestand van de applicatie bevat:
            saveFileDialog1.InitialDirectory=Application.ExecutablePath
  • We stellen de weer te geven bestandstypen in
            saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
  • We stellen het bestandstype in dat als eerste moet worden weergegeven
            saveFileDialog1.FilterIndex = 0

Hier worden bestanden van het type *.txt als eerste aan de gebruiker getoond.

  • Het dialoogvenster wordt weergegeven en het resultaat wordt opgehaald

    If openFileDialog1.ShowDialog() = DialogResult.OK Then

Terwijl het dialoogvenster wordt weergegeven, heeft de gebruiker geen toegang meer tot het hoofdformulier (dit wordt een modaal dialoogvenster genoemd). De gebruiker geeft de naam op van het te openen bestand en verlaat het dialoogvenster door op de knop Openen of Annuleren te klikken, of door het venster te sluiten. Het resultaat van de methode ShowDialog is alleen DialogResult.OK als de gebruiker de knop Ouvrir heeft gebruikt om het dialoogvenster te sluiten.

  • Zodra dit is gebeurd, staat de naam van het aan te maken bestand nu in de eigenschap FileName van het object openFileDialog1. We keren dan terug naar het klassieke lezen van een tekstbestand. Let op de methode waarmee het volledige bestand kan worden gelezen:
                    txtTexte.Text=fichier.ReadToEnd
  • de inhoud van het bestand wordt opgeslagen in de TextBox en txtTexte. Eventuele uitzonderingen worden afgehandeld.

5.7.2. Dialoogvensters FontColor en ColorDialog

We gaan verder met het vorige voorbeeld en introduceren twee nieuwe knoppen:

Nr.
type
naam
functie
6
Knop
btnCouleur
om de kleur van de tekens van TextBox vast te stellen
7
Knop
btnPolice
om het lettertype van TextBox in te stellen

We plaatsen een ColorDialog-besturingselement en een FontDialog-besturingselement op het formulier:

Image

De klassen FontDialog en ColorDialog hebben een methode ShowDialog die vergelijkbaar is met de methode ShowDialog van de klassen OpenFileDialog en SaveFileDialog. Met de methode ShowDialog van de klasse ColorDialog kan een kleur worden gekozen:

Image

Als de gebruiker het dialoogvenster verlaat met de knop OK, is het resultaat van de methode ShowDialog DialogResult.OK en staat de gekozen kleur in de eigenschap Color van het gebruikte object ColorDialog. Met de methode ShowDialog van de klasse FontDialog kan een lettertype worden gekozen:

Image

Als de gebruiker het dialoogvenster sluit met de knop OK, is het resultaat van de methode ShowDialog DialogResult.OK en staat het gekozen lettertype in de eigenschap Font van het gebruikte object FontDialog. We hebben de elementen om klikken op de knoppen Couleur en Police te verwerken:


    Private Sub btnCouleur_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCouleur.Click
        ' tekstkleur kiezen
        If colorDialog1.ShowDialog() = DialogResult.OK Then
            ' de eigenschap 'forecolor' van TextBox wordt gewijzigd
            txtTexte.ForeColor = colorDialog1.Color
        End If
    End Sub


    Private Sub btnPolice_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnPolice.Click
        ' een lettertype kiezen
        If fontDialog1.ShowDialog() = DialogResult.OK Then
            ' de eigenschap `font` van TextBox wijzigen
            txtTexte.Font = fontDialog1.Font
        End If
    End Sub

5.7.3. Timer

We stellen hier voor om de volgende applicatie te schrijven:

nr.
Type
Naam
Rol
1
TextBox, ReadOnly=true
txtChrono
toont een stopwatch
2
Knop
btnArretMarche
knop Stop/Start van de stopwatch
3
Timer
timer1
component die hier elke seconde een gebeurtenis uitzendt

De stopwatch loopt:

Image

De stopwatch is gestopt:

Image

Om de inhoud van TextBox en txtChrono elke seconde te wijzigen, hebben we een component nodig die elke seconde een gebeurtenis genereert. Deze gebeurtenis kunnen we vervolgens opvangen om de weergave van de stopwatch bij te werken. Die component is de Timer:

Image

Zodra deze component op het formulier is geïnstalleerd (in het gedeelte met niet-visuele componenten), wordt er een object van het type Timer aangemaakt in de formulierbouwer. De klasse System.Windows.Forms.Timer is als volgt gedefinieerd:

Image

Van de eigenschappen ervan zullen we alleen de volgende onthouden:

Interval
aantal milliseconden waarna een gebeurtenis Tick wordt gegenereerd.
Tick
de gebeurtenis die plaatsvindt na Interval milliseconden
Enabled
maakt de timer actief (true) of inactief (false)

In ons voorbeeld heet de timer timer1 en is timer1.Interval ingesteld op 1000 ms (1 s). De gebeurtenis Tick zal dus elke seconde plaatsvinden. Het klikken op de knop Stop/Start wordt verwerkt door de volgende procedure:


    Private Sub btnArretMarche_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnArretMarche.Click
        ' uit of aan?
        If btnArretMarche.Text = "Marche" Then
            ' de starttijd noteren
            début = DateTime.Now
            ' we geven deze weer
            txtChrono.Text = "00:00:00"
            ' de timer wordt gestart
            timer1.Enabled = True
            ' de tekst van de knop wordt gewijzigd
            btnArretMarche.Text = "Arrêt"
            ' einde
            Return
        End If        '
        If btnArretMarche.Text = "Arrêt" Then
            ' de timer stoppen
            timer1.Enabled = False
            ' de tekst van de knop wordt gewijzigd
            btnArretMarche.Text = "Marche"
            ' einde
            Return
        End If
    End Sub

    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' er is één seconde verstreken
        Dim maintenant As DateTime = DateTime.Now
        Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
        txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
    End Sub

De tekst op de knop Stop/Start is ofwel „Stop“ ofwel „Start“. We moeten dus een controle uitvoeren op deze tekst om te bepalen wat er moet gebeuren.

  • In het geval van „Aan” wordt het starttijdstip opgeslagen in een variabele die een globale variabele van het formulierobject is, wordt de timer gestart (Enabled=true) en verandert de tekst van de knop in „Uit”.
  • In het geval van "Stop" wordt de timer gestopt (Enabled=false) en wordt de tekst op de knop gewijzigd in "Start".

Public Class Timer1
  Inherits System.Windows.Forms.Form
  Private WithEvents timer1 As System.Windows.Forms.Timer
  Private WithEvents btnArretMarche As System.Windows.Forms.Button
  Private components As System.ComponentModel.IContainer
  Private WithEvents txtChrono As System.Windows.Forms.TextBox
  Private WithEvents label1 As System.Windows.Forms.Label

  ' instantievariabelen
  Private début As DateTime

Het bovenstaande attribuut début is bekend in alle methoden van de klasse. We moeten nu nog de gebeurtenis Tick op het object timer1 afhandelen, een gebeurtenis die elke seconde plaatsvindt:


    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' er is één seconde verstreken
        Dim maintenant As DateTime = DateTime.Now
        Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
        txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
    End Sub

We berekenen de tijd die is verstreken sinds het starten van de chronometer. We krijgen een object van het type TimeSpan dat een tijdsduur vertegenwoordigt. Deze moet in de stopwatch worden weergegeven in de vorm hh:mm:ss. Hiervoor gebruiken we de eigenschappen Hours, Minutes, Seconds van het object TimeSPan, die respectievelijk de uren, minuten en seconden van de tijdsduur vertegenwoordigen. We geven deze weer in het formaat ToString("d2") om een weergave met twee cijfers te krijgen.

5.8. Het voorbeeld IMPOTS

We pakken de applicatie IMPOTS er weer bij, die we al twee keer hebben behandeld. We voegen er nu een grafische interface aan toe:

De besturingselementen zijn als volgt

nr.
type
naam
rol
1
RadioButton
rdOui
aangevinkt indien gehuwd
2
RadioButton
rdNon
aangevinkt indien niet gehuwd
3
NumericUpDown
incEnfants
aantal kinderen van de belastingplichtige
Minimum=0, Maximum=20, Stap=1
4
TextBox
txtSalaire
jaarsalaris van de belastingplichtige in F
5
TextBox
txtImpots
te betalen belastingbedrag
ReadOnly=true
6
Knop
btnCalculer
start de belastingberekening
7
Knop
btnEffacer
zet het formulier bij het laden terug naar de oorspronkelijke staat
8
Knop
btnQuitter
om de applicatie te verlaten

Werkingsregels

  • de knop 'Berekenen' blijft uitgeschakeld zolang er niets in het salarisveld staat
  • als bij het starten van de berekening blijkt dat het salaris onjuist is, wordt de fout gemeld:

Image

Het programma staat hieronder. Het maakt gebruik van de klasse impot die in het hoofdstuk over klassen is aangemaakt. Een deel van de code die automatisch door VS.NET wordt gegenereerd, is hier niet weergegeven.


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data

' formulierklasse
Public Class frmImpots
  Inherits System.Windows.Forms.Form
  Private WithEvents label1 As System.Windows.Forms.Label
  Private WithEvents rdOui As System.Windows.Forms.RadioButton
  Private WithEvents rdNon As System.Windows.Forms.RadioButton
  Private WithEvents label2 As System.Windows.Forms.Label
  Private WithEvents txtSalaire As System.Windows.Forms.TextBox
  Private WithEvents label3 As System.Windows.Forms.Label
  Private WithEvents label4 As System.Windows.Forms.Label
  Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
  Private WithEvents btnCalculer As System.Windows.Forms.Button
  Private WithEvents btnEffacer As System.Windows.Forms.Button
  Private WithEvents btnQuitter As System.Windows.Forms.Button
  Private WithEvents txtImpots As System.Windows.Forms.TextBox
  Private components As System.ComponentModel.Container = Nothing
  Private WithEvents incEnfants As System.Windows.Forms.NumericUpDown

  ' gegevenstabellen die nodig zijn voor de berekening van de belasting
  Private limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
  Private coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.55D, 0.5D, 0.6D, 0.65D}
  Private coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}
   ' belastingobject
  Private objImpôt As impot = Nothing


  Public Sub New()
    InitializeComponent()
     ' initialisatie van het formulier
    btnEffacer_Click(Nothing, Nothing)
    btnCalculer.Enabled = False
     ' aanmaken van een belastingobject
    Try
      objImpôt = New impot(limites, coeffR, coeffN)
    Catch ex As Exception
      MessageBox.Show("Impossible de créer l'objet impôt (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
       ' het invoerveld voor het salaris wordt uitgeschakeld
      txtSalaire.Enabled = False
    End Try 'try-catch
  End Sub 

  Protected Overloads Sub Dispose(ByVal disposing As Boolean)
....
  End Sub

  Private Sub InitializeComponent()
    Me.btnQuitter = New System.Windows.Forms.Button
    Me.groupBox1 = New System.Windows.Forms.GroupBox
    Me.btnEffacer = New System.Windows.Forms.Button
    Me.btnCalculer = New System.Windows.Forms.Button
    Me.txtSalaire = New System.Windows.Forms.TextBox
    Me.label1 = New System.Windows.Forms.Label
    Me.label2 = New System.Windows.Forms.Label
    Me.label3 = New System.Windows.Forms.Label
    Me.rdNon = New System.Windows.Forms.RadioButton
    Me.txtImpots = New System.Windows.Forms.TextBox
    Me.label4 = New System.Windows.Forms.Label
    Me.rdOui = New System.Windows.Forms.RadioButton
    Me.incEnfants = New System.Windows.Forms.NumericUpDown
    Me.groupBox1.SuspendLayout()
    CType(Me.incEnfants, System.ComponentModel.ISupportInitialize).BeginInit()
    Me.SuspendLayout()
....
  End Sub 'InitializeComponent

  Public Shared Sub Main()
    Application.Run(New frmImpots)
  End Sub 'Main


  Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnEffacer.Click
     ' formulier leegmaken
    incEnfants.Value = 0
    txtSalaire.Text = ""
    txtImpots.Text = ""
    rdNon.Checked = True
  End Sub 'btnEffacer_Click


  Private Sub txtSalaire_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles txtSalaire.TextChanged
     ' status van de knop 'Berekenen'
    btnCalculer.Enabled = txtSalaire.Text.Trim() <> ""
  End Sub 'txtSalaire_TextChanged


  Private Sub btnQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnQuitter.Click
     ' einde van de toepassing
    Application.Exit()
  End Sub 'btnQuitter_Click


  Private Sub btnCalculer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnCalculer.Click
     ' Is het salaris correct?
    Dim intSalaire As Integer = 0
    Try
      ' het salaris ophalen
      intSalaire = Integer.Parse(txtSalaire.Text)
       ' moet >=0 zijn
      If intSalaire < 0 Then
        Throw New Exception("")
      End If
    Catch ex As Exception
      ' foutmelding
      MessageBox.Show(Me, "Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error)
       ' focus op foutief veld
      txtSalaire.Focus()
       ' tekst in invoerveld selecteren
      txtSalaire.SelectAll()
       ' terug naar de visuele interface
      Return
    End Try 'try-catch
    ' het salaris is correct – de belasting wordt berekend
    txtImpots.Text = "" & CLng(objImpôt.calculer(rdOui.Checked, CInt(incEnfants.Value), intSalaire))
  End Sub 'btnCalculer_Click
End Class

We gebruiken hier de assemblage impots.dll, het resultaat van de compilatie van de klasse impots uit hoofdstuk 2. Ter herinnering: deze assemblage kan in de consolemodus worden gegenereerd met het commando

dos>vbc /t:library impots.vb

Dit commando genereert het bestand impots.dll, ook wel ‘assemblage’ genoemd. Deze assemblage kan vervolgens in verschillende projecten worden gebruikt. Hier, in ons project onder VS.NET, gebruiken we het venster met de projecteigenschappen:

Image

Om een verwijzing (een assemblage) toe te voegen, klikken we met de rechtermuisknop op de sleutelgroep ‘References’ hierboven, kiezen we de optie [Ajouter une référence] en selecteren we de assemblage [impots.dll] die we zorgvuldig in de projectmap hebben geplaatst:

dos>dir
01/03/2004  14:39                9 250 gui_impots.vb
01/03/2004  14:37                4 096 impots.dll
01/03/2004  14:41               12 288 gui_impots.exe

Zodra de assembly [impots.dll] in het project is opgenomen, wordt de klasse [impots] bekend bij het project. Voorheen was dat niet het geval. Een andere methode is om de broncode impots.vb in het project op te nemen. Hiervoor klikt men in het venster met de projecteigenschappen met de rechtermuisknop op het project, kiest men de optie [Ajouter/Ajouter un élément existant] en selecteert men het bestand impots.vb.