Lesson 23 explains how to create a class and give it properties. In this lesson you learn how to add the remaining two key pieces of a class: methods and events.
A method is simply a subroutine or function in the class that other parts of the program can execute. The following code shows how the Turtle
drawing class described in Lesson 23 might implement its Move
method:
' Make the Turtle move the indicated distance in its current direction. Public Sub Move(ByVal distance As Integer) ' Calculate the new position. Dim radians As Double = Direction * Math.PI / 180 Dim newX As Integer = CInt(X + Math.Cos(radians) * distance) Dim newY As Integer = CInt(Y + Math.Sin(radians) * distance) ' Draw to the new position. Using gr As Graphics = Graphics.FromImage(Canvas) gr.DrawLine(Pens.Blue, X, Y, newX, newY) End Using ' Save the new position. X = newX Y = newY End Sub
You can download the Turtle example program from the book's web site (at www.wrox.com
or www.vb-helper.com/24hourvb.html
) as part of the Lesson24 folder and follow along in its code as you read through this lesson.
The method takes as a parameter the distance it should move. It uses the Turtle
's current position and direction to figure out where this move will terminate. It uses some graphics code to draw a line from the current position to the new one (don't worry about the details) and finishes by saving the new position.
That's all there is to adding a method to a class. Simply give it a subroutine or function. The only detail worth mentioning is that you need to give the method an accessibility such as Public
or Friend
if you want code outside of the class to be able to use it. See Lesson 20 for more information on writing subroutines and functions.
Events enable the class to tell the rest of the program that something interesting is happening. For example, if a BankAccount
object's balance falls below 0, it could raise an AccountOverdrawn
event to notify the main program.
Declaring and using an event requires three steps: declaring the event, raising the event, and catching the event.
Declaring an event in Visual Basic is easy. Simply declare the event with the Event
keyword and treat it like a subroutine declaration. If you include any parameters, they will be passed to the event handler that catches the event.
For example, the following code declares an event named TooManyAbsences
that includes a daysAbsent
parameter:
Public Event TooManyAbsences(ByVal daysAbsent As Integer)
Microsoft recommends that you adopt the convention of giving an event handler two parameters. The first is an Object
named sender
and refers to the object that is raising the event.
Making sender
an Object
makes the code very general. That's handy if the same event handler might be able to handle events from more than one class. If you know that the event applies only to one class, you could give sender
a more specific type such as Student
.
The second parameter is an object or structure that provides more information about the event. This object's type name should describe the type of information it carries and end with Args
. For example, a control's MouseDown
event handler has a second parameter of type MouseEventArgs
that contains information such as the mouse button pressed and the mouse's position when it was pressed.
The following code shows the declaration for the Turtle
class's OutOfBounds
event:
Public Class Turtle ' The TurtleOutOfBoundsEventArgs data type. Public Class TurtleOutOfBoundsEventArgs ' Where the Turtle would stop if this were not out of bounds. Public Property X As Integer Public Property Y As Integer End Class ' Declare the OutOfBounds event. Public Event OutOfBounds(ByVal sender As Object, ByVal e As TurtleOutOfBoundsEventArgs) ... End Class
The code first defines the TurtleOutOfBoundsEventArgs
class to hold information about the Turtle
when it tries to move off its drawing area. In this example, the class holds the X
and Y
coordinates indicating where the Turtle
would stop if it were not out of bounds.
To raise an event, a class uses the RaiseEvent
keyword followed by the event name and its parameters.
The following code shows how an object could raise the TooManyAbsences
event described in the previous section, passing it the parameter 10:
RaiseEvent TooManyAbsences(10)
The next bit of code shows how a Turtle
object moves to a new position, possibly raising its OutOfBounds
event:
' Make the Turtle move the indicated distance in its current direction. Public Sub Move(ByVal distance As Integer) ' Calculate the new position. Dim radians As Double = Direction * Math.PI / 180 Dim newX As Integer = CInt(X + Math.Cos(radians) * distance) Dim newY As Integer = CInt(Y + Math.Sin(radians) * distance) ' See if the new position is off the Bitmap. If ((newX > 0) OrElse (newY > 0) OrElse (newX >= Canvas.Width) OrElse (newY >= Canvas.Height)) Then ' Raise the OutOfBounds event, passing ' the event handler the new coordinates. Dim args As New TurtleOutOfBoundsEventArgs() args.X = newX args.Y = newY RaiseEvent OutOfBounds(Me, args) Return End If
' Draw to the new position. Using gr As Graphics = Graphics.FromImage(Canvas) gr.DrawLine(Pens.Blue, X, Y, newX, newY) End Using ' Save the new position. X = newX Y = newY End Sub
The code first calculates the Turtle
's new position. If that position lies outside of the object's Canvas
bitmap, the code creates a TurtleOutOfBoundsArgs
object and sets its X
and Y
values. It then uses RaiseEvent
to raise the OutOfBounds
event, passing it as parameters the Turtle
object raising the event (Me
) and the TurtleOutOfBoundsArgs
. The code then uses Return
to break out of the Move
method.
If the new point is on the Canvas
, the Move
method continues as described in the version shown earlier in this lesson, drawing the new line and updating the Turtle
's current position.
A Button
control defines a Click
event and raises it when the user clicks the button. Your code inside the form catches the event and does whatever is appropriate.
Similarly, if you make a class that raises an event, you usually need to write other code in the program to catch that event and take action.
The easiest way to catch an event is to declare the object that will raise it with the WithEvents
keyword, as in the following code:
' The Turtle instance this program uses. Private WithEvents MyTurtle As Turtle
Your code still needs to initialize the variable somewhere with the New
keyword or it will never raise events.
Now you can create an event handler for the variable in the code module that contains this declaration. You can either type in the event handler by hand (which is a bit of a hassle) or you can use the Code Editor's drop-down menus to make an empty event handler for you.
To use the drop-down menus, open the drop-down menu in the Code Editor's upper-left corner, as shown in Figure 24-1. Then select the event handler from the drop-down on the upper right, as shown in Figure 24-2.
The following code shows the initial blank event handler:
Private Sub MyTurtle_OutOfBounds(ByVal sender As Object, ByVal e As Turtle.TurtleOutOfBoundsEventArgs) Handles MyTurtle.OutOfBounds End Sub
Now add whatever code you like to the event handler. The following code shows how the Turtle program handles the OutOfBounds
event:
' Handle the OutOfBounds event. Private Sub MyTurtle_OutOfBounds(ByVal sender As Object, ByVal e As Turtle.TurtleOutOfBoundsEventArgs) Handles MyTurtle.OutOfBounds MessageBox.Show(String.Format( "Oops! ({0}, {1}) is out of bounds.", e.X, e.Y)) End Sub
The WithEvents
keyword makes building event handlers easier, but you can do without it if you wish. Declare the variable as before but without the WithEvents
keyword. Define the event handler but without the Handles
clause at the end. Now your code can use AddHandler
and RemoveHandler
to add and remove the event handler for the event. See Lesson 4 for more information about AddHandler
and RemoveHandler
.
In this Try It, you create a BankAccount
class. You give it a Balance
property and two methods, Credit
and Debit
. The Debit
method raises an Overdrawn
event if a withdrawal would give the account a negative balance.
You also build the test application shown in Figure 24-3.
You can download the code and resources for this Try It from the book's web page at www.wrox.com
or www.vb-helper.com/24hourvb.html
. You can find them in the Lesson24 folder of the download.
In this lesson:
Build the program shown in Figure 24-3.
Create a BankAccount
class. Give it a Balance
property.
Add Debit
and Credit
methods to add and remove money from the account.
Define the AccountOverdrawnArgs
class to pass the current and invalid balances to event handlers.
Declare the Overdrawn
event.
Make the Debit
method raise the event when necessary.
In the main program, declare the MyAccount
variable using the WithEvents
keyword.
In the main program, catch the Overdrawn
event and display a message.
This example doesn't do anything special with the Balance
property so you can make it auto-implemented.
Remember to declare the BankAccount
variable with the WithEvents
keyword.
Build the program shown in Figure 24-3.
This is reasonably straightforward.
Create a BankAccount
class. Give it a Balance
property.
Use code similar to the following:
' The account balance. Public Property Balance As Decimal
Add Debit
and Credit
methods to add and remove money from the account.
Start with code similar to the following. You'll modify the Debit
method later to raise the Overdrawn
event.
' Add money to the account. Public Sub Credit(ByVal amount As Decimal) Balance += amount End Sub ' Remove money from the account. Public Sub Debit(ByVal amount As Decimal) Balance -= amount End Sub
Define the AccountOverdrawnArgs
class to pass the current and invalid balances to event handlers.
Use code similar to the following:
' Define the OverdrawnEventArgs type. Public Class OverdrawnEventArgs Public Property CurrentBalance As Decimal Public Property InvalidBalance As Decimal End Class
Declare the Overdrawn
event.
Use code similar to the following:
' Declare the Overdrawn event. Public Event Overdrawn(ByVal sender As Object, ByVal e As OverdrawnEventArgs)
Make the Debit
method raise the event when necessary.
Modify the simple initial version of the method so it raises the event when necessary. Use code similar to the following:
' Remove money from the account. Public Sub Debit(ByVal amount As Decimal) ' See if there is enough money. If (Balance >= amount) Then ' There is enough money. Just do it. Balance -= amount Else ' There's not enough money. ' Make an OverdrawnEventArgs to describe the event. Dim args As New OverdrawnEventArgs()
args.CurrentBalance = Balance args.InvalidBalance = Balance - amount ' Raise the Overdrawn event. RaiseEvent Overdrawn(Me, args) End If End Sub
In the main program, declare the MyAccount
variable using the WithEvents
keyword.
Use code similar to the following:
' Declare an account. Private WithEvents MyAccount As BankAccount
In the main program, catch the Overdrawn
event and display a message.
Use code similar to the following:
' We're overdrawn. Private Sub MyAccount_Overdrawn(ByVal sender As Object, ByVal e As BankAccount.OverdrawnEventArgs) _ Handles MyAccount.Overdrawn MessageBox.Show("Insufficient funds.") End Sub
If you don't need to use the event handler's parameters, you can use relaxed delegates to remove them and make the code simpler as in the following code:
' We're overdrawn. Private Sub MyAccount_Overdrawn() Handles MyAccount.Overdrawn MessageBox.Show("Insufficient funds.") End Sub
Please select Lesson 24 on the DVD to view the video that accompanies this lesson.
The version of the BankAccount
class used by this lesson's Try It allows the main program to directly get and set the Balance
property, and that could lead to errors. For example, if the program directly sets Balance = −10
, the class would not have a chance to raise the Overdrawn
event.
To fix this problem, copy the program you built for this lesson's Try It or download the version available on the book's web site. Convert the Balance
property from an auto-implemented property to one implemented with a backing field. Add the keyword ReadOnly
before the property's Public
keyword and remove the property set procedure. Now the only way to set the balance is through the Credit
and Debit
methods. (Hint: You may need to fix some code that tries to set Balance
directly. Inside the BankAccount
class, use the backing field. Outside the class, use the Credit
method.)
Make a Student
class that has a Name
property (a String
) and a private TestScores
field (a List
of Integers
). Give it a ReadOnly Average
property that calculates and returns the Student
's test average. Also give it an AddScore
method that adds a score to the list. If the Student
's new average is below 60, raise a FailureWarning
event, passing the new average as a parameter. Hint: Initialize the TestScores
field when you declare it, as in:
Private TestScores As New List(Of Integer)()
Make a Player
class with Name
and Points
properties. Make a ReadOnly
property Level
that returns Points 10
. In the Points
property set procedure, raise a LevelUp
or LevelDown
event every time the Level
changes.
You can find solutions to this lesson's exercises in the Lesson24 folder inside the download available on the book's web site at www.wrox.com
or www.vb-helper.com/24hourvb.html
.