Visual Basic 2012 Voorbeelden
   

visual basic 2012 broncode voorbeelden

Blijf op de hoogte van de recente aanpassingen op vbvoorbeelden!

Microsoft Visual Studio 2012Microsoft Developers Network - Visual BasicMicrosoft .NET Framework

24.2. Events

Print Email Deel op Twitter Deel op Facebook

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.

Een applicatie kan je bekijken als een reeks van routines die na elkaar worden aangeroepen.  Tot nu toe hebben we steeds een applicatie vorm gegeven door in de code (al dan niet gebaseerd op invoer van de gebruiker) te bepalen welke routine na welke andere procedure wordt uitgevoerd.

In de realiteit is dit vaak anders, het valt vaak voor dat applicatie ( of althans grote delen daarvan) "eventdriven" zijn.  Waarmee bedoeld wordt dat het optreden van die gebeurtenissen eigenlijk het programmaverloop gaan bepalen.

Events zijn gebeurtenissen die kunnen optreden in een applicatie.

Een aantal voorbeelden van dergelijke events die kunnen optreden : form_load (bij het laden van het venster), button_click (bij het klikken op een knop), textbox_changed (bij het wijzigen van de inhoud van een tekstvak), ...
Dit zijn allemaal voorbeelden van events die optreden bij het gebruiken van de grafische user interface (GUI) van een applicatie.

Maar ook andere soorten van events zijn nuttig en bruikbaar, bijvoorbeeld : "berekening uitgevoerd", "data uit database ingeladen", ...

We kunnen onze eigen klassen ook "eventsources" ("eventsenders") maken.
We doen dit om clients van deze klasse op basis van het optreden van dergelijke gebeurtenis/event een bepaalde actie te laten uitvoeren.  Deze actie kunnen we definiëren via een "eventhandler", die gekoppeld wordt aan een event van een eventsource.

24.2.1. Event Definitie

Een event voegen we toe aan een eventsource op volgende wijze : accessmodifier Event identifier(argumentslist) type_specifier

Deze eventsource zal de event doen optreden via een RaiseEvent statement : RaiseEvent identifier(argumentslist)
Visual Basic 2012 Broncode - Codevoorbeeld 632
Option Strict On
Option Explicit On
Namespace Example1
    Class SomeEventSource
        Delegate Sub SomeDelegate()
        Public Event SomeEvent As SomeDelegate
        Public Sub RaiseSomeEvent()
            RaiseEvent SomeEvent()
        End Sub
    End Class
End Namespace

24.2.2. Koppeling at Compiletime - WithEvents Veld en Handles Clausule

Een client kan bij het gebruik van een eventsource het optreden van de event ( moment van RaiseEvent) koppelen aan een bepaalde actie (eventhandler, of ook "eventtarget" genoemd).
Deze (statische) koppeling kan at compiletime gebeuren door de eventsource als WithEvents veld te declareren, en de eventhandler te voorzien van een Handles clausule die verwijst naar de event van de eventsource die wordt afgehandeld.
Visual Basic 2012 Broncode - Codevoorbeeld 633
Namespace Example1
    Class Client
        Private Shared WithEvents _SomeEventSource As New SomeEventSource
        Public Shared Sub Main()
            _SomeEventSource.RaiseSomeEvent()
            '
            Console.ReadLine()
        End Sub
        Private Shared Sub someEventHandler() _
                                             Handles _SomeEventSource.SomeEvent
            Console.WriteLine("someEventHandler")
        End Sub
    End Class
End Namespace
Console Application Output
someEventHandler
Een eventhandler is steeds een procedure, met of zonder ("gewone") argumenten, optionele argumenten of parameterarrays zijn niet toegestaan.

De eigenlijke eventsource is het object waarnaartoe het WithEvents veld verwijst.
Visual Basic 2012 Broncode - Codevoorbeeld 634
Namespace Example2
    Class SomeEventSource
        Public Property ID As Integer
        '
        Delegate Sub SomeDelegate(ByVal sender As SomeEventSource)
        Public Event SomeEvent As SomeDelegate
        Public Sub RaiseSomeEvent()
            RaiseEvent SomeEvent(Me)
        End Sub
    End Class
    Class Client
        Private Shared WithEvents _SomeEventSource As SomeEventSource
        Public Shared Sub Main()
            Dim object1 As New SomeEventSource With {.ID = 1}
            Dim object2 As New SomeEventSource With {.ID = 2}
            '
            _SomeEventSource = object1
            _SomeEventSource.RaiseSomeEvent()                             ' (1)
            object1.RaiseSomeEvent()                                       ' (2)
            '
            _SomeEventSource = object2
            _SomeEventSource.RaiseSomeEvent()                             ' (3)
            object2.RaiseSomeEvent()                                       ' (4)
            '
            object1.RaiseSomeEvent()                                       ' (5)
            '
            Console.ReadLine()
        End Sub
        Private Shared Sub someEventHandler(ByVal sender As SomeEventSource) _
                                             Handles _SomeEventSource.SomeEvent
            Console.WriteLine("someEventHandler " & _
                              "( eventsource id : " & sender.ID & ")")
        End Sub
        Private Shared Sub setEventSource( _
                                       ByVal someEventSource As SomeEventSource)
            _SomeEventSource = SomeEventSource
        End Sub
    End Class
End Namespace
Console Application Output
someEventHandler (eventsource id : 1)
someEventHandler (eventsource id : 1)
someEventHandler (eventsource id : 2)
someEventHandler (eventsource id : 2)
Enkel het object waarnaartoe het WithEvents veld _SomeEventSource verwijst is de aan een eventhandler gekoppelde eventsource.  Op regels (1), (2), (3) en (4) verwijzen de expressies _SomeEventSource, object1 en object2 dan ook naar de eigenlijke eventsource.  De object1 expressie op regel (5) echter, verwijst naar een instantie van SomeEventSource die niet meer gekoppeld is aan de eventhandler.

Een typisch gebruik van argumenten voor een event wordt in bovenstaand voorbeeld geïllustreerd.  Het sender argument is van het eventsourcetype, en kan zo door de eventhandler worden gebruikt zijn afhandeling te baseren op de identiteit of toestand van het eigenlijke eventsource object.

24.2.3. Eventhandling Pattern

Een generiek EventHandler(Of TEventArgs As System.EventArgs)-event type is reeds voorgedefinieerd.  Deze is bruikbaar indien je het eventhandling pattern van het .NET Framework wenst toe te passen (1).

Dit pattern bestaat erin bij het optreden van de event de eventsource door te geven (zoals in vorig voorbeeld) en ook de "eventarguments" door te geven.  Dit doorgeven van deze event argumenten zou in de vorm van een afgeleide EventArgs moeten gebeuren.
Dit eventargumententype fungeert als containertype voor de eventargumenten die extra informatie over de event of eventsource kunnen opleveren naar de eventhandler toe.

Bij het doen optreden van deze event door de eventsource, wordt het eventsource object zelf (Me) en een instantie van het generiek actueel parametertype (TEventArgs) doorgegeven.

De eventhandler van de client dien een Object-source argument en een TEventArgs argument te definiëren.

Het actueel generiek parametertype (TEventArgs) moet afgeleid zijn van het voorgedefinieerde type System.EventArgs (2).
Visual Basic 2012 Broncode - Codevoorbeeld 635
Namespace Example3
    Class Product
        'Public Delegate Sub PriceChangedHandler(ByVal sender As Object, _
        '                                      ByVal e As PriceChangedEventArgs)
        'Public Event PriceChanged As PriceChangedHandler
        ' or shorter :
        Public Event PriceChanged As EventHandler(Of PriceChangedEventArgs)
        '
        Protected Sub OnPriceChanged()
            RaiseEvent PriceChanged(Me, New PriceChangedEventArgs(Price))
        End Sub
        '
        Private _Price As Decimal
        Public Property Price() As Decimal
            Get
                Price = _Price
            End Get
            Set(ByVal value As Decimal)
                _Price = value
                OnPriceChanged()
            End Set
        End Property
    End Class
    Class PriceChangedEventArgs : Inherits EventArgs
        Private ReadOnly _Price As Decimal
        Public Sub New(ByVal price As Decimal)
            _Price = price
        End Sub
        Public ReadOnly Property Price() As Decimal
            Get
                Price = _Price
            End Get
        End Property
    End Class
    Class Client
        Private Shared WithEvents product1 As New Product
        Public Shared Sub Main()
            product1.Price = 5
            product1.Price = 10
            '
            Console.ReadLine()
        End Sub
        Private Shared Sub product1_PriceChanged( _
                     ByVal sender As Object, ByVal e As PriceChangedEventArgs) _
                                                   Handles product1.PriceChanged
            Console.WriteLine("New price : " & e.Price)
        End Sub
    End Class
End Namespace
Console Application Output
New price : 5
New price : 10
In onderstaand voorbeeld wordt geïllustreerd hoe :

- een eventsource meerdere events kan bevatten
- een eventhandler aan meerdere events van meerdere eventsources kan gekoppeld worden
- een event van een eventsource aan meerdere eventhandlers kan gekoppeld worden
- niet alle events van een eventsource moeten afgehandeld worden
- de volgorde van optreden van statisch gekoppelde eventhandlers onvoorspelbaar is
Visual Basic 2012 Broncode - Codevoorbeeld 636
Namespace Example4
    Class SomeFirstEventSource
        Public Event SomeFirstEvent()
        Public Event SomeSecondEvent()
        Public Sub RaiseEvents()
            RaiseEvent SomeFirstEvent()
            RaiseEvent SomeSecondEvent()
        End Sub
    End Class
    Class SomeSecondEventSource
        Public Event SomeFirstEvent()
        Public Event SomeSecondEvent()
        Public Sub RaiseEvents()
            RaiseEvent SomeFirstEvent()
            RaiseEvent SomeSecondEvent()
        End Sub
    End Class
    Class Client
        Private Shared WithEvents _SomeFirstEventSource As _
                                                        New SomeFirstEventSource
        Private Shared WithEvents _SomeSecondEventSource As _
                                                       New SomeSecondEventSource
        Public Shared Sub Main()
            _SomeFirstEventSource.RaiseEvents()
            _SomeSecondEventSource.RaiseEvents()
            '
            Console.ReadLine()
        End Sub
        Private Shared Sub someFirstEventHandler() _
                                Handles _SomeFirstEventSource.SomeFirstEvent, _
                                        _SomeFirstEventSource.SomeSecondEvent
            Console.WriteLine("someFirstEventHandler")
        End Sub
        Private Shared Sub someSecondEventHandler() _
                                Handles _SomeFirstEventSource.SomeFirstEvent, _
                                        _SomeSecondEventSource.SomeSecondEvent
            Console.WriteLine("someSecondEventHandler")
        End Sub
    End Class
End Namespace
Console Application Output
someSecondEventHandler
someFirstEventHandler
someFirstEventHandler
someSecondEventHandler

24.2.4. Koppeling at Runtime - AddHandler en RemoveHandler

In voorgaande voorbeelden werden de events van de eventsources steeds statisch (at compiletime) gekoppeld aan de eventhandlers.  Ook dynamische koppeling (at runtime) is mogelijk.  Deze werkwijze is zelfs flexibeler.

We hebben geen WithEvents gedeclareerde velden nodig (die de eventsources zijn) en we dienen bij de eventhandler geen Handles clause op te nemen.

Wat we wel nodig hebben is een AddHandler statement om aan te geven ( tijdens runtime) welke procedure we koppelen aan welke event van welke eventsource.
Eens dit statement uitgevoerd is de koppeling gemaakt en zal bij het optreden van die event van die eventsource de eventhandler worden aangeroepen.

De mogelijkheid bestaat nu (in tegenstelling tot bij statische koppeling) om die eventhandler weer los te koppelen van de event van die eventsource.
Dit kan aan de hand van een RemoveHandler statement, waarbij je dezelfde informatie opgeeft als bij de AddHandler.

Het is een flexibelere oplossing in de zin dat at runtime kan bepaald worden wanneer en welke routine een event kan afhandelen.

In tegenstelling tot statische koppeling waarbij eender welk object waarnaartoe de WithEvents member wijst de eventsource is, is bij dynamisch koppeling elk object waarnaartoe je als eventsource verwijst in een AddHandler statement een eventsource.
Visual Basic 2012 Broncode - Codevoorbeeld 637
Namespace Example5
    Class SomeEventArgs : Inherits EventArgs
        Private ReadOnly _EventArgsCounter As Integer
        Public Sub New(ByVal counter As Integer)
            _EventArgsCounter = counter
        End Sub
        Public ReadOnly Property EventCounter() As Integer
            Get
                EventCounter = _EventArgsCounter
            End Get
        End Property
    End Class
    Class SomeEventSource
        Public Property ID As Integer
        Public Event SomeEvent As EventHandler(Of SomeEventArgs)
        Public Sub RaiseSomeEvent()
            Static counter As Integer = 1
            RaiseEvent SomeEvent(Me, New SomeEventArgs(counter))
            counter += 1
        End Sub
    End Class
    Class Client
        Public Shared Sub Main()
            Dim someFirstEventSource As New SomeEventSource With {.ID = 1}
            Dim someSecondEventSource As SomeEventSource
            '
            someFirstEventSource.RaiseSomeEvent()  ' /
            '
            AddHandler someFirstEventSource.SomeEvent, AddressOf someEventHandler
            someFirstEventSource.RaiseSomeEvent()  ' "handler id : 1, counter : 2"
            '
            someSecondEventSource = someFirstEventSource
            someFirstEventSource.RaiseSomeEvent()  ' "handler id : 1, counter : 3"
            someSecondEventSource.RaiseSomeEvent() ' "handler id : 1, counter : 4"
            '
            someFirstEventSource = New SomeEventSource With {.ID = 2}
            someFirstEventSource.RaiseSomeEvent()  ' /
            someSecondEventSource.RaiseSomeEvent() ' "handler id : 1, counter : 5"
            '
            AddHandler someFirstEventSource.SomeEvent, AddressOf someEventHandler
            someFirstEventSource.RaiseSomeEvent()  ' "handler id : 2, counter : 2"
            someSecondEventSource.RaiseSomeEvent() ' "handler id : 1, counter : 6"
            '
            RemoveHandler someSecondEventSource.SomeEvent, _
                                                         AddressOf someEventHandler
            someFirstEventSource.RaiseSomeEvent()  ' "handler id : 2, counter : 3"
            someSecondEventSource.RaiseSomeEvent() ' /
            '
            AddHandler someFirstEventSource.SomeEvent, AddressOf someEventHandler
            someFirstEventSource.RaiseSomeEvent()  ' "handler id : 2, counter : 4"
            '                                        "handler id : 2, counter : 4"
            '
            RemoveHandler someFirstEventSource.SomeEvent, _
                                                         AddressOf someEventHandler
            someFirstEventSource.RaiseSomeEvent()  ' "handler id : 2, counter : 5"
            '
            Console.ReadLine()
        End Sub
        Private Shared Sub someEventHandler(ByVal sender As Object, _
                                            ByVal e As SomeEventArgs)
            Console.WriteLine("someEventHandler " & _
                              "( eventsource id : " & _
                              DirectCast(sender, SomeEventSource).ID & ", " & _
                              "event-counter : " & e.EventCounter & ")")
        End Sub
    End Class
End Namespace
Console Application Output
someEventHandler (eventsource id : 1, event-counter : 2)
someEventHandler (eventsource id : 1, event-counter : 3)
someEventHandler (eventsource id : 1, event-counter : 4)
someEventHandler (eventsource id : 1, event-counter : 5)
someEventHandler (eventsource id : 2, event-counter : 2)
someEventHandler (eventsource id : 1, event-counter : 6)
someEventHandler (eventsource id : 2, event-counter : 3)
someEventHandler (eventsource id : 2, event-counter : 4)
someEventHandler (eventsource id : 2, event-counter : 4)
someEventHandler (eventsource id : 2, event-counter : 5)
Ook bij koppeling at runtime kan :

- een eventsource meerdere events bevatten
- een eventhandler aan meerdere events van meerdere eventsources worden gekoppeld
- een event van een eventsource aan meerdere eventhandlers worden gekoppeld
- niet alle events van een eventsource afgehandeld worden

De volgorde van optreden van de dynamisch gekoppelde eventhandlers is hier wel duidelijk, ze zullen in dezelfde volgorde als volgorde van koppeling worden uitgevoerd.

Als verschillende malen dezelfde eventhandler gekoppeld werd aan een event van dezelfde eventsource, zal bij het verwijderen van die eventhandler van die event, slechts een van die koppelingen verwijderd worden.

24.2.5. Overerven van Events

Een afgeleide klasse (SomeDerivedEventSource) erft events over van de basis klasse (SomeBaseEventSource).  Als de method RaiseEvents op een SomeDerivedEventSource object wordt aangeroepen, zullen dan ook de overgeërfde events optreden.

Men kan geen overgeërfde events "raisen", een event moet men altijd doen optreden vanuit het type waarin de event gedefenieerd is.
Vandaar dat er vaak een Protected "OnEvent" method wordt voorzien die de event zal raisen in de eventsource.  Deze protected method kan dan wel worden aangeroepen in de afgeleide eventsource.

Het is mogelijk in de eventsource zelf (of in afgeleide types) een eventhandler te voorzien en te koppelen aan een event van deze eventsource.
Deze koppeling kan zowel at run- als at compiletime gebeuren.
Visual Basic 2012 Broncode - Codevoorbeeld 638
Namespace Example6
    Class SomeBaseEventSource
        Public Event SomeFirstEvent()
        Protected Event someSecondEvent()
        Private Event someThirdEvent()
        Private Sub someEventHandler() Handles Me.someThirdEvent
            Console.WriteLine("SomeEventSource.someEventHandler")
        End Sub
        Public Sub RaiseEvents()
            RaiseEvent SomeFirstEvent()
            RaiseEvent someSecondEvent()
        End Sub
    End Class
    Class SomeDerivedEventSource : Inherits SomeBaseEventSource
        Private Sub someEventHandler() Handles MyBase.someSecondEvent
            Console.WriteLine("SomeDerivedEventSource.someEventHandler")
        End Sub
    End Class
    Class Client
        Private Shared WithEvents _SomeDerivedEventSource As _
                                                    New SomeDerivedEventSource()
        Public Shared Sub Main()
            _SomeDerivedEventSource.RaiseEvents()
            '
            Console.ReadLine()
        End Sub
        Private Shared Sub someEventHandler() _
                                 Handles _SomeDerivedEventSource.SomeFirstEvent
            Console.WriteLine("someEventHandler")
        End Sub
    End Class
End Namespace
Console Application Output
someEventHandler
SomeDerivedEventSource.someEventHandler
Toch is het op zen minst gesteld vreemd de events van een eventsource in de eventsource zelf af te handelen.  Events worden typisch gebruikt in situaties waar het type (in dit geval eventsourcetype) niet beschikt over de kennis wat er moet gebeuren als deze event zich voordoet.  Een Button bijvoorbeeld weet immers ook niet wat er moet gebeuren bij het klikken op deze knop binnen een specifieke applicatie die van deze knop gebruikt maakt.

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.