voorbeeldbestand
Controle op gebruikersinvoer in Userform


1 Inleiding

2.1 Userform methode I
2.2 Userform methode II
2.3 Userform methode III
2.3.1 Klassemodule
2.3.2 Code in de klassemodule
2.3.3 Code in het Userform
2.3.4 met een 'Collection'
2.3.5 met een 'Array'

3 Beperkingen

4 Toelichting voorbeeldbestand

5 Verwante onderwerpen

6 Voorbeeldbestanden

1 Inleiding

Als je gebruikersinvoer wil begeleiden/ondersteunen/controleren kun je in een Officeprogramma gebruik maken van een Userform.
Als je alle denkbare 'vergissingen' van gebruikers wil ondervangen merk je dat je snel heel veel coderegels nodig hebt.
Met het gebruik van een klassemodule kun je dat flink beperken.
Dat geldt ook voor VBA-controle op invoer in ActiveX-objekten in een sheet.
Voor een uitleg daarvan zie deze pagina.

De uitleg van de werkwijze gebeurt hier aan de hand van tekstvakken in een Userform.
Gebruikersinvoer leidt bijvoorbeeld tot wijziging van de inhoud van een tekstvak (TextBox).
Om de invoer te controleren kun je gebruik maken van de gebeurtenis 'Change' van een tekstvak.
Iedere wijziging in het tekstvak door de gebruiker aktiveert deze controle.
We gaan hier uit, zoals in het voorbeeldbestand, van een userform met 10 tekstvakken.
Deze tekstvakken hebben de naam tekst1, tekst2.. ...t/m tekst10.

2.1 Userform methode I

In het userform kun je deze code zetten om te controleren of alle 10 tekstvakken in het Userform tekst bevatten.
Als dat het geval is wordt de knop 'Vervolg' zichtbaar.
Private Sub Tekst1_Change()
For j=1 To 10
If Trim(Me("Tekst" & j).Text)="" Then Exit For
Next
Me("knop_vervolg").Visible = j >10
End Sub

Private Sub Tekst2_Change()
For j=1 To 10
If Trim(Me("Tekst" & j).Text)="" Then Exit For
Next
Me("knop_vervolg").Visible = j >10
End Sub

Private Sub Tekst3_Change()
For j=1 to 10
If Trim(Me("Tekst" & j).Text)="" Then Exit For
Next
Me("knop_vervolg").Visible= j >10
End Sub

etc.

2.2 Userform methode II

Het wordt al efficiënter met:
Private Sub Tekst1_Change()
vervolg
End Sub

Private Sub Tekst2_Change()
vervolg
End Sub

Private Sub Tekst3_Change()
vervolg
End Sub

Private sub vervolg()
For j=1 to 10
If Trim(Me("Tekst" & j).Text)="" Then Exit For
Next
Me("knop_vervolg").Visible = j >10
End Sub
Maar nog steeds moet je bij ieder tekstvak in de gebeurtenis _Change aangeven wat er moet gebeuren.

2.3 Userform methode III

Dan zou je nog het volgende kunnen bedenken:
- zet alle tekstvakken als objecten in een verzameling: een 'Collection' of een 'Array'
- koppel de controlecode (zoals hierboven de macro 'vervolg') aan de -Change-gebeurtenis van ieder element in deze verzameling
Alle koppelingen zitten dan in een verzameling.
Iedere wijziging van een element aktiveert dan de koppeling in de verzameling.

Een koppeling met een gebeurtenis kun je alleen maar leggen met een 'WithEvents' variable.
Die kun je echter alleen in een klassemodule gebruiken.
Daarom zul je voor zo'n methode gebruik moeten maken van een klassemodule.

2.3.1 Klassemodule

Hoe werkt zo'n klassemodule in dit geval ?
Omdat ieder besturingselement in een Userform 'eigen' gebeurtenissen heeft, moet je voor ieder soort besturingselement in de klassemodule een aparte gebeurtenisvariabele definiëren
Je koppelt met VBA alle tekstvakken in het userform via een verzameling aan die overeenkomstige gebeurtenisvariabele in de klassemodule.
In de klassemodule zet je dan de (gebeurtenis)code die van toepassing is op de gebeurtenisvariabele van de klassemodule.

NB. een klassemodule kan voor nog veel meer zaken gebruikt worden, maar hier beperken we ons tot het 'afvangen' van gebeurtenissen van besturingselementen in een userform.

2.3.2 Wat staat er in de klassemodule ?

1. Een publieke object-variabele waaraan een bepaald userformobject (in dit geval een tekstvak) kan worden toegewezen: in het voorbeeld de variabele cl_tekstvak.

Omdat het om het afvangen van gebeurtenissen (Events) gaat moeten we die variabele nader specificeren met de instruktie WithEvents.
Public WithEvents cl_tekstvak As MSForms.TextBox
2. Eén procedure per 'gebeurtenis' van de objectvariable cl_tekstvak. (Dat kunnen natuurlijk alleen de gebeurtenissen zijn die van toepassing zijn op een TextBox).

In het voorbeeld van de bijlage staan o.a. de gebeurtenissen _Change en _DblClick:
Private Sub cl_tekstvak_Change()
-------
End Sub

Private Sub cl_tekstvak_DblClick()
-----
End Sub
NB. Deze enkele _Change procedure in de klassemodule vervangt zo alle _Change procedures die we eerst in het userform hadden staan.

2.3.3 Wat staat er in het Userform ?

Omdat alle controles op wat de gebruiker in het userform doet in de klassemodule staan, is de userformmodule wel erg leeg (maar dat was ook de bedoeling).

2.3.4 Gebruik een 'Collection'

Declareer een 'public' variabele als een nieuwe 'collection'
Public ctl_verzameling as New Collection
De naam voor deze variable is willekeurig. Om aan te geven dat het een verzameling van 'controls' gaat, geef je bijv. het pr&‚;fix 'ctl_'.

In de gebeurtenis Userfom_Initialize:
Private Sub Userform_Initialize()
For Each ct in Controls
ctl_verzameling.Add New invoercontrole, ct.Name
If TypeName(ct)="TextBox" Then Set ctl_verzameling(ct.Name).cl_tekstvak = ct
Next
End Sub
Voor ieder te controleren userformobjekt voeg je een koppeling met de klassemodule (technisch: een instantie van de klassemodule) toe aan de collection 'verzameling'.
Die koppeling krijgt hier de naam van het userformobject als sleutel, zodat je de naam ook kunt gebruiken om het element uit de collection op te roepen.
Als je de sleutel achterwege laat kun je alleen de index gebuiken om een element te benaderen.
ctl_verzameling.add New invoercontrole, ct.Name
Wijs het tekstvak via die koppeling in de collection toe aan de 'WithEvents"-variabele ('cl_tekstvak') in de klassemodule.
set ctl_verzameling(ct.Name).cl_tekstvak = ct
Als de gebruiker nu een element van de verzameling wijzigt aktiveert deze koppeling de code van de klassemodule.

2.3.5 Gebruik een 'Array'

Declareer een private 1-dimensionele array als een nieuwe instantie van de klassemodule.
Dim sr() as New invoercontrole
of
Private sr() as New invoercontrole
In de gebeurtenis Userfom_Initialize:
Private Sub Userform_Initialize()
ReDim sr(Controls.Count-1)

For j = 0 to Controls.Count - 1
If TypeName(Controls(j)) = "TextBox" Then Set sr(j).cl_tekstvak = Controls(j)
Next
End Sub
Maak de array zo groot als het aantal besturingselementen in het userform
ReDim sr(Controls.Count)
Omdat de array gedeclareerd is als nieuwe klassemodule bestaat nu ieder element van de array uit een eigen instantie van de klassemodule.

Elementen van een array zijn alleen met de index te benaderen.
Ieder besturingselement van het userform moet in een apart element van de array terechtkomen.
Daarom gebruiken we hier de For j ..Next lus.
Met een For Each lus zouden we een extra teller nodig hebben.

Controleer om wat voor type besturingselement het gaat (met de methode 'typename') om de koppeling met de juiste WithEvents-variabele te kunnen leggen.
Wijs het tekstvak via die koppeling toe aan de 'WithEvents'-variabele ('cl_tekstvak') van de klassemodule.
Set sr(j).cl_tekstvak=Controls(j)
Als de gebruiker nu iets wijzigt in een tekstvak in het userform wordt via de instanties in array 'sr' de koppeling met de code van de klassemodule geaktiveerd.

3 Beperkingen

Niet alle gebeurtenisssen van besturingselementen kunnen via een WithEvents-variable worden 'afgevangen'
Dat geldt voor de volgende gebeurtenissen:

- Activate
- Deactivate
- QueryClose
- Resize
- Terminate
- AfterUpdate
- BeforeUpdate
- Enter
- Exit

4 Toelichting op het voorbeeldbestand

Het voorbeeld bestand bevat:
- de klassemodule 'invoercontrole'
- het userform 'scherm collection', dat gebruik maakt van de 'collection' methode.
- het userform 'scherm array' dat gebruik maakt van de 'array' methode.
Het bestand illustreert tegelijkertijd dat de code in een klassemodule voor diverse userforms tegelijkertijd dienst kan doen.

De klassemodule 'invoercontrole'

De klassemodule bevat WithEvents-variabelen voor:
- Label
- TextBox
- ComboBox
- ListBox
- CheckBox
- OptionButton
- CommandButton
- SpinButton
- MultiPage
- Frame
- UserForm
De klassemodule bevat per besturingselement alle gebeurtenissen die via een klassemodule kunnen worden afgevangen.
Voor ieder soort besturingelement staat een eigen code in de gebeurteniscode _Click.
Aan de Textbox_change gebeurtenis is de controlemacro 'A_vervolg' gekoppeld die nagaat of alle tekstvakken zijn gevuld; als dat het geval is wordt de 'knop_vervolg' zichtbaar.
Bij alle andere gebeurtenissen staat een code waarmee een MsgBox kan worden getoond bij activering; deze code is met een apostrof uitgeschakeld.
Door deze aprostrof te verwijderen kan de werking van iedere afzonderlijke gebeurteniscode worden getest.

Ter illustratie zijn verschillende soorten code bij de _click gebeurtenis van de labels in het userform opgenomen:
- het wijzigen van de inhoud van het besturingselement zelf
- het wijzigen van de vormgeving van het besturingselement zelf
- het wijzigen van een ander besturingselement
- het starten van een macro
- het aanroepen van een funktie
- het wijzigen van het werkblad

Het userform 'scherm_collection'

Het werkblad bevat een knop dat dit userform toont.
Het userform bevat van alle besturingselementen 10 exemplaren, behalve van de ListBox, de Multipage en het Frame.
Het userform 'scherm_collection' is identiek aan het userform 'scherm_array'; het verschil zit in de VBA-code: het gebruik van de 'collection' methode (zie de userform_Initialize gebeurtenis).
Dit userform gedraagt het zich identiek aan het userform 'scherm_array' omdat het gebruik maakt van dezelfde klassemodule.
In de code voor de afhandeling van de textbox_change gebeurtenis staat een voorbeeld hoe een in het userform public gedeclareerde collection in de klassemodule kan worden uitgelezen.

Het userform 'scherm_array'

Het werkblad bevat een knop dat dit userform toont.
Het userform bevat van alle besturingselementen 10 exemplaren, behalve van de ListBox, de Multipage en het Frame.
Het userform 'scherm_array' is identiek aan het userform 'scherm_collection'; het verschil zit in de VBA-code: het gebruik van de 'array' methode (zie de userform_Initialize gebeurtenis).
Omdat dit userform gebruik maakt van dezelfde klassemodule 'invoercontrole' gedraagt het zich identiek aan het userform 'scherm_collection'.

5 Verwante onderwerpen

VBA controle van ActiveX-objekten in een werkblad

6 Voorbeeldbestanden voor elk type userformcontrol

Label
Textbox
Combobox
Listbox
Checkbox
Optionbutton
Commandbutton
Spinbutton
Frame
Multipage
ToggleButton
Scrollbar