Understanding Custom Events using Visual Basic.NET 2005
This article teaches you how to declare and handle events using Visual Basic.NET 2005. It assumes the reader is familiar with object-oriented programming in Visual Basic.NET.
The entire source code for this article is available in the form of a downloadable zip file. The solution was developed using Microsoft Visual Studio 2005 Professional Edition on Microsoft Windows Server 2003 Enterprise Edition. I didn't really test it in any other environment. I request that you post in the discussion area if you have any problems in execution.
A sample class without any event declarations: coding the class
Before going directly to learning about events in Visual Basic 2005, let us try to understand the following class:
Public Class Sample01
Private _x As Integer
Private _y As Integer
Public Property X() As Integer
Get
Return _x
End Get
Set(ByVal value As Integer)
_x = value
End Set
End Property
Public Property Y() As Integer
Get
Return _y
End Get
Set(ByVal value As Integer)
_y = value
End Set
End Property
Public Function GetCalculatedValue() As Double
If Me.Y <> 0 Then
Dim v As Double = Me.X / Me.Y
MessageBox.Show("Successful")
Return v
Else 'division by zero
MessageBox.Show("Division by zero is not permitted")
Return 0
End If
End Function
End Class
The above class has two private fields ("_x" and "_y") which are only accessible within the class, and not outside the class. Further, it has two public properties and a public method named "GetCalculatedValue" (public members are accessible even outside the class).
The "GetCalculatedValue" method tries to do division based on the values available in "_x" and "_y." The denominator ("_y") should never be zero during division. The "if" condition checks for the same and responds with an appropriate message.
In the next section we will try to test the above class with a Windows form.
To test the class presented in the previous section, add a new form with two buttons and a label. Modify your code to match the following:
Public Class Form1
Dim obj As New Sample01
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
obj.X = 10
obj.Y = 0
Me.lblValue.Text = obj.GetCalculatedValue
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
obj.X = 10
obj.Y = 5
Me.lblValue.Text = obj.GetCalculatedValue
End Sub
End Class
When you hit "Button1," it should give you a "Successful" message and the result in a label. If you hit "Button2," it gives the error message and the result in the label would be "0."
It is quite impractical (and not at all recommended) to have message boxes in classes. The business logic classes are meant only for logic and none of them should display any message boxes. Instead, the messages should be sent to the calling applications (in this case, it is the form).
In the "GetCalculatedValue" method, we are dealing with two things simultaneously. One of them is giving a message to the form (the user may use the message for display or simply suppress messages from displaying), and the other is returning a value back to the form. Returning a value to the form is simply done using the "Return" statement. How about the message? How do we send it back professionally? The user may or may not be interested in the message! If he is interested, he displays the message in another label and if not, he simply neglects it.
The next section gives you a professional approach for the same issue using events in Visual Basic 2005.
The following is the class which is rewritten to support events available in Visual Basic 2005. Before trying to understand events, let us go through the following class:
Public Class Sample01
Private _x As Integer
Private _y As Integer
Public Event CalculationStatus(ByVal Msg As String)
Public Property X() As Integer
Get
Return _x
End Get
Set(ByVal value As Integer)
_x = value
End Set
End Property
Public Property Y() As Integer
Get
Return _y
End Get
Set(ByVal value As Integer)
_y = value
End Set
End Property
Public Function GetCalculatedValue() As Double
If Me.Y <> 0 Then
Dim v As Double = Me.X / Me.Y
RaiseEvent CalculationStatus("Successful")
Return v
Else 'division by zero
RaiseEvent CalculationStatus("Division by zero is not permitted")
Return 0
End If
End Function
End Class
Let us try to understand the above class. The following is a new statement in the above class:
Public Event CalculationStatus(ByVal Msg As String)
The above declaration says that "CalculationStatus" is an event (part of "Sample01" class) which may be necessary to handle at the client level (say Windows Form). It carries a message with the "Msg" parameter.
Every event declared within the class must be fired to notify the client (say Windows Form) to handle the latest event. If the event is not fired, the client would never know about its message or progress. To fire an event, "RaiseEvent" will be used. In the above class, we are firing two times in various situations (as part of an "if" condition).
If you drag a button onto Windows Form, you can observe that it has lots of events to handle. But mostly you will handle only the "Click" event. Even the "Click" event is not compulsory to handle. If you don't handle any events, the button simply does nothing. Similarly, with our class, when instantiated, you have the opportunity to handle the "CalculationStatus" event (which is still not compulsory). If the Windows Form handles the event, it gives out the message; otherwise, it does not.
In the next section we try to test the above class with a Windows Form.
To test the class presented in the previous section, add a new form with two buttons and two labels (one to display the result after division and the other for the message). Modify your code to match the following:
Public Class Form2
WithEvents obj As New Sample01
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
obj.X = 10
obj.Y = 5
Me.lblValue.Text = obj.GetCalculatedValue
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
obj.X = 10
obj.Y = 0
Me.lblValue.Text = obj.GetCalculatedValue
End Sub
Private Sub obj_CalculationStatus(ByVal Msg As String) Handles obj.CalculationStatus
Me.lblMsg.Text = Msg
End Sub
End Class
Usually to instantiate an object, we would write the following:
Dim obj As New Sample01
But to handle events along with instantiation, it should be written as follows:
WithEvents obj As New Sample01
That means "obj" has one or more events to handle and we can handle them as part of our code at the client level. To handle the "CalculationStatus" event of "obj" the following is newly added to the above code:
Private Sub obj_CalculationStatus(ByVal Msg As String) Handles obj.CalculationStatus
Me.lblMsg.Text = Msg
End Sub
When the "RaiseEvent" is executed in the "Sample01" class, the event gets fired and the control automatically executes the above method (say the method which handles the event), which finally displays the message. If you don't write the above method (say you don't handle the event), you simply miss the messages, but the calculation still proceeds.
In previous section, if you observe the following statement, it is declared at the class level, which means that it is outside all the methods:
WithEvents obj As New Sample01
This may not be convenient in every situation as we do create a lot of objects and remove them every time, especially in loops. The "WithEvents" cannot be available as part of method in a class. It can only be available with class level variables/objects.
To solve the above situation, the "AddHandler" comes to the rescue. Let us rewrite our testing class as follows:
Public Class Form2
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Public Sub Msg_CalculationStatus(ByVal Msg As String)
Me.lblMsg.Text = Msg
End Sub
End Class
From the above class, you can observe that no instance is declared at the class level. The object instantiation is done separately in each method and they get automatically erased from memory once the execution completes.
To attach a method to an object dynamically (to handle an event), we should use the "AddHandler" statement as follows:
The above statement says that the "CalculationStatus" event of the "obj" object will be handled by the "Msg_CalculationStatus" method. And further, the same method can handle any number of events, even when attached to several objects. Make sure that the signature of this method matches with that of the event.
Finally, you can dynamically remove the handler using "RemoveHandler," if it is already attached using "AddHandler".
In my next article, I shall introduce Delegates in Visual Basic 2005. I hope you enjoyed the article and any suggestions, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com