1. Computing

Events Using Your Own Code in VB.NET

Write the event code yourself!

By

Updated April 17, 2012

When Visual Basic first emerged onto the programming scene ... (we're talking "age of dinosaurs" now) ... it was called event oriented programming and you still see that description used sometimes. That's a testament to how important events are to every part of programming as it's done today. I think it's as important as John Von Neuman's "stored program concept" back in 1945. (That's before the dinosaurs!)

About Visual Basic has a number of articles that cover different parts of event-oriented programming ...

-> Handling Events in VB.NET
-> Program and Handle Your Own Unique Event
-> Creating Your Own Event Code
-> Using Delegates for Runtime Flexibility
-> Shared Events in VB.NET

Taken together, these cover most of the subject. But you have to put the pieces together yourself to get the whole picture. And they generally use Framework objects - like Button and Timer - to illustrate the use of events. This is the first of a two article series that builds the whole thing in one place and uses a class that is coded inside the example program so you can be sure that nothing is hidden. Part Two, Using the Custom Event Keyword, builds on this same theme by explaining how really custom events work.

The example program used in this article is inspired by a problem brought to an "in person" programming class I taught at a college recently. "Ralph" helped manage a table tennis tournament and wanted to upgrade some VB programs he used. So this program keeps score for one match in the tournament. The "event" that is coded happens when a player wins the match.

In addition to a complete, custom coded event, you might also be interested in this article as an example of ...

-> The use of an XML file to persist data
-> LINQ to XML to read and write the XML file
-> And, of course, the use of a custom class, which is simply another way of describing custom event code

Because a lot of people will just want to get to example code without wading through a lot of text, here's the complete program, followed by the XML file. Explanation will follow.


Public Class TTMatchControl
    Dim PlayerA As String = "George"
    Dim PlayerB As String = "Arthur"
    Private WithEvents theMatch As TableTennisMatch
    Private Sub Form1_Load(
        sender As System.Object,
        e As System.EventArgs
        ) Handles MyBase.Load
        theMatch = New TableTennisMatch
        lblPlayerA.Text = PlayerA
        lblPlayerB.Text = PlayerB
        Dim PStats = XElement.Load("..\..\PlayerStats.xml")
        Dim pStat =
            From p In PStats.<PStat>
            Select p
            Where p.<PID>.Value = PlayerA
        lblPlayerAWins.Text = pStat.<Won>.Value
        pStat =
            From p In PStats.<PStat>
            Select p
            Where p.<PID>.Value = PlayerB
        lblPlayerBWins.Text = pStat.<Won>.Value
    End Sub
    Private Sub btnAddPointA_Click(
        sender As System.Object,
        e As System.EventArgs
        ) Handles btnAddPointA.Click
        theMatch.UpdateScore(0)
        lblPlayerAScore.Text =
            CStr(theMatch.PlayerAScore)
    End Sub
    Private Sub btnAddPointB_Click(
        sender As System.Object,
        e As System.EventArgs
        ) Handles btnAddPointB.Click
        theMatch.UpdateScore(1)
        lblPlayerBScore.Text =
            CStr(theMatch.PlayerBScore)
    End Sub
    Public Sub Record_WinLoss(
        ByVal ww As Integer
        ) Handles theMatch.MatchWon
        Dim PStats =
            XElement.Load("..\..\PlayerStats.xml")
        Dim pStatA =
            From p In PStats.<PStat>
            Select p
            Where p.<PID>.Value = PlayerA

        Dim pStatB =
            From p In PStats.<PStat>
            Select p
            Where p.<PID>.Value = PlayerB

        If ww = 0 Then
            pStatA.<Won>.Value += 1
            pStatB.<Lost>.Value += 1
            lblPlayerWon.Text = PlayerA & " Wins!"
        Else
            pStatB.<Won>.Value += 1
            pStatA.<Lost>.Value += 1
            lblPlayerWon.Text = PlayerB & " Wins!"
        End If
        PStats.Save("..\..\PlayerStats.xml")

        lblPlayerBWins.Text = pStatB.<Won>.Value
        lblPlayerAWins.Text = pStatA.<Won>.Value
        btnAddPointA.Enabled = False
        btnAddPointB.Enabled = False
    End Sub
End Class

Public Class TableTennisMatch
    Event MatchWon(whoWon As Integer)
    Property PlayerAScore As Integer
    Property PlayerBScore As Integer
    Public Sub UpdateScore(ByVal pNum As Integer)
        If pNum = 0 Then
            PlayerAScore += 1
        Else
            PlayerBScore += 1
        End If
        If PlayerAScore > 20 Then
            If PlayerAScore >
                PlayerBScore + 1 Then
                RaiseEvent MatchWon(0)
            End If
        End If
        If PlayerBScore > 20 Then
            If PlayerBScore >
                PlayerAScore + 1 Then
                RaiseEvent MatchWon(1)
            End If
        End If
    End Sub
End Class

The XML file ... (PlayerStats.xml)

<?xml version="1.0" encoding="utf-8"?>
<PStats>
  <PStat>
    <PID>George</PID>
    <Won>17</Won>
    <Lost>8</Lost>
  </PStat>
  <PStat>
    <PID>Arthur</PID>
    <Won>9</Won>
    <Lost>20</Lost>
  </PStat>
</PStats>

The example only has one form, shown below:

--------
Click Here to display the illustration
--------

The example starts with two players who will face off in a table tennis match. In a more practical application, these two players will also be in a data file (possibly an XML file to match the scores maintained in this file). In this example, string values for the two players ("George" and "Arthur") are simply hard coded.

The next statement ...


Private WithEvents theMatch As TableTennisMatch

... is a requirement for setting up a custom event. "theMatch" is coded later as an instance of the custom class, "TableTennisMatch". The keyword "WithEvents" tells the compiler to add code to the instance of a class that is created ...


theMatch = New TableTennisMatch

... later in the code. This is why WithEvents variables must be coded at the class level. This code added by the compiler will insert messages into the Windows "message pump" so another piece of code can be automatically called when the event occurs. The code that is called is identified by the Handles keyword ...


Public Sub Record_WinLoss(
    ByVal ww As Integer
    ) Handles theMatch.MatchWon

This is the custom event code that is the topic item of this article. The article Creating Your Own Event Code goes into more detail about this.

As an added example, the code also shows how easy it is to load, read, and update an XML file using the very SQL-like syntax of LINQ to XML introduced in Framework 3.5.

The event that signals when a match is won or lost is raised in the instantiated class TableTennisMatch. This class keeps track of the score in properties and whenever the score is changed, it checks to see if the game is over. If it is, the MatchWon() event is raised with a type Integer parameter that tells the code handling the event (Record_WinLoss) which player won the match. This code updates the XML file, announces the win, and disables the buttons.

The following illustration shows the event related code and how statements in the two classes tie together ... in this version.

--------
Click Here to display the illustration
--------

An alternative way to code this solution would be to explicitly code a delegate. I decided to put it completely outside the existing classes. That way, the scope would include those classes. I also coded the instantiation of the class, TableTennisMatch, as part of the TTMatchControl class rather than the form where it's used.


Public Delegate Sub MatchWonHandler(ww As Integer)
Public Class TTMatchControl
    Dim theMatch As New TableTennisMatch

To wireup the event handler to the event, I used an AddHandler statement instead of the Handles clause on the event handler subroutine itself.


AddHandler theMatch.MatchWon, AddressOf Me.Record_WinLoss

...

Public Sub Record_WinLoss(
    ByVal ww As Integer
    ) ' Note - No Handles clause

Then the Event itself refers to the delegate - MatchWon As MatchWonHandler - rather than specifying a hardcoded parameter - MatchWon(whoWon As Integer).


Public Class TableTennisMatch
    Public Event MatchWon As MatchWonHandler

The use of a delegate and the AddHandler statement gives you extra flexibility. Although this example is a little 'forced' to match the table tennis metaphor I'm using, you could add two event handlers now, one for the players and another to signal that the table is available for another match. Since the first parameter will signal which player won, I added another parameter for the table.


Public Delegate Sub MatchWonHandler(
    ww As Integer, tt As Integer)

Then both handlers have to be added, not just one. The AddHandler statement is a little bit unusual in the way it works here. A linked list of all handlers that have been added is maintained by VB.NET and executed one at a time. In some cases, this can be a problem because later handlers won't run until earlier ones finish and this can be a blocking problem. I'll cover this a bit more in the second part of this series.


AddHandler theMatch.MatchWon, AddressOf Me.Record_WinLoss
AddHandler theMatch.MatchWon, AddressOf Me.UpdateTable

The RaiseEvent statement has to be modified.


RaiseEvent MatchWon(0, Table)

Finally, another event processing subroutine has to be present. In this case, I just added text to a label, but a complete system to update a "table" database and possibly raise another event could be coded here.


Public Sub UpdateTable(
    ByVal ww As Integer,
    ByVal tt As Integer)
    lblUpdateTable.Text =
        "Table " & tt &
        vbCrLf & " is available."
End Sub

--------
Click Here to display the illustration
--------

VB.NET actually handles all events in pretty much the same way, but with most options, much of the code that makes it work is added by the compiler and isn't shown. You can add your own code to replace this automatic code, but understanding how it ties together isn't obvious ... and (Surprise!) Microsoft's documentation doesn't help a lot. Part Two of this series covers some of these mysteries and explains how the "Custom" keywork in declaring your event changes things.

To continue this series: Using the Custom Event Keyword

  1. About.com
  2. Computing
  3. Visual Basic
  4. Using VB.NET
  5. VB.NET Custom Events

©2014 About.com. All rights reserved.