Events

Team-Fly    

 
Application Development Using Visual Basic and .NET
By Robert J. Oberg, Peter Thorsteinson, Dana L. Wyatt
Table of Contents
Chapter 6.  VB.NET in the .NET Framework


Delegates are the foundation for a design pattern known as events . Conceptually, servers implement incoming interfaces, which are called by clients . In a diagram, such an interface may be shown with a small bubble (a notation used in COM). Sometimes a client may wish to receive notifications from a server when certain "events" occur. In such a case the server will specify an outgoing interface. The server defines the interface and the client implements it. In a diagram, such an interface may be shown with an arrow (again, a notation used in COM). Figure 6-4 illustrates a server with one incoming and one outgoing interface. In the case of the outgoing interface, the client will implement an incoming interface, which the server will call.

Figure 6-4. A server with an incoming interface and an outgoing interface.

graphics/06fig04.gif

A good example of a programming situation with events is a graphical user interface. An event is some external action, typically triggered by the user, to which the program must respond. Events include user actions such as clicking a mouse button or pressing a key on the keyboard. A GUI program must contain event handlers to respond to or "handle" these events. We will see many examples of GUI event handling in Chapter 7, where we discuss Windows Forms, and we'll look at a simple Windows Forms example in the next section.

Events in Visual Basic and VB.NET

From the very beginning, Visual Basic has provided an easy-to-use mechanism for dealing with events. Consider the following VB6 program MouseEventVb6 , which displays a blank form and puts up a message box whenever the user clicks the mouse.

 graphics/codeexample.gif  Private Sub Form_Click()  MsgBox ("Mouse was clicked!") End Sub 

This code is simple but somewhat inflexible , based on a naming convention. The Click event is tied to the Form base class by the name Form_Click .

By contrast, consider the comparable VB.NET program MouseEventVbNet .

 graphics/codeexample.gif Public Class Form1    Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " ...    Private Sub Form1_Click(ByVal sender As Object, _     ByVal e As EventArgs)  Handles MyBase.Click  MessageBox.Show("Mouse was clicked!")    End Sub End Class 

The VB.NET code uses the Handles keyword to identify the event. Any name can be used for the handler function. In fact, you can have several functions handling the same event, reflecting the fact that VB.NET events use multicast delegates under the hood.

The example code we have just examined illustrates static event handling , which is easy to use and follows a syntax that is a natural extension of VB6 event handling. VB.NET also supports dynamic event handling , where event handlers can be added or removed at runtime. We will look at dynamic event handling first, because it is tied closely to the underlying delegate mechanism that we have studied. We will then discuss static event handling.

Dynamic Event Handling

The .NET Framework provides an easy-to-use implementation of the event paradigm built on delegates. VB.NET simplifies working with .NET events by providing the keyword Event and the AddHandler and RemoveHandler keywords to hook up event handlers to events and to remove them. The Framework also defines a base class EventArgs to be used for passing arguments to event handlers. There are a number of derived classes defined by the Framework for specific types of events, such as MouseEventArgs , ListChangedEventArgs , and so forth. These derived classes define data members to hold appropriate argument information.

An event handler is a delegate with a specific signature,

 Public Delegate Sub EventHandler(_  ByVal sender As Object, ByVal e As EventArgs) 

The name EventHandler is arbitrary. The first argument represents the source of the event, and the second argument contains data associated with the event.

We will examine this dynamic event architecture in the example program EventDemo\Step1 , which implements a simple chat room.

Server-Side Event Code

We begin with server-side code, in ChatServer.vb . The .NET event architecture uses delegates of a specific signature. Two delegates are declared in this example.

 graphics/codeexample.gif Public Delegate Sub  JoinHandler  (_  ByVal sender As Object, ByVal e As ChatEventArg) Public Delegate Sub  QuitHandler  (_  ByVal sender As Object, ByVal e As ChatEventArg) 

The first parameter specifies the object that sent the event notification. The second parameter is used to pass data along with the notification. Typically, you will derive a class from EventArg to hold your event specific data.

 Public Class ChatEventArg    Inherits EventArgs    Public m_name As String    Public Sub New(ByVal name As String)       m_name = name    End Sub End Class 

Two delegate object references are declared using the keyword Event .

 Public Class ChatServer    ...    Public Event Join As JoinHandler    Public Event Quit As QuitHandler End Class 

Helper methods are typically provided to facilitate calling the delegate object(s) that have been hooked up to the event. The RaiseEvent keyword is used to actually call the delegate via the event.

 Protected Sub  OnJoin  (ByVal e As ChatEventArg)  RaiseEvent  Join(Me, e) End Sub Protected Sub  OnQuit  (ByVal e As ChatEventArg)  RaiseEvent  Quit(Me, e) End Sub 

Typically, access is specified as Protected so that a derived class has access to this helper method, whereas external code has no business calling it. You can then "fire" the event by calling the helper method at any point in any method of your event source class. The EventDemo\Version 1 example happens to do this in methods named JoinChat and QuitChat .

 Public Sub  JoinChat  (ByVal name As String)    m_members.Add(name)  OnJoin  (New ChatEventArg(name)) 'fire the Join event End Sub Public Sub  QuitChat  (ByVal name As String)    m_members.Remove(name)  OnQuit  (New ChatEventArg(name)) 'fire the Quit event End Sub 
Client-Side Event Code

The client provides event handler functions. These are the callback functions called by the server.

 Public Sub OnJoinChat(_  ByVal sender As Object, _  ByVal e As ChatEventArg)    Console.WriteLine(_       "sender = {0}, {1} has joined the chat", _       sender, _       e.m_name) End Sub Public Sub OnQuitChat(_  ByVal sender As Object, _  ByVal e As ChatEventArg)    Console.WriteLine(_       "sender = {0}, {1} has quit the chat", _       sender, e.m_name) End Sub 

The client hooks the handlers to the events, using the AddHandler keyword. Once the handlers are established, the client may choose to join or quit the chat server at will.

 Sub Main()    Dim chat As ChatServer = _       New ChatServer("OI Chat Room")    ' Register to receive event notifications  AddHandler chat.Join, AddressOf OnJoinChat   AddHandler chat.Quit, AddressOf OnQuitChat  ' Call methods on the server    chat.JoinChat("Michael")    chat.JoinChat("Bob")    chat.JoinChat("Sam")    chat.ShowMembers("After 3 have joined")    chat.QuitChat("Bob")    chat.ShowMembers("After 1 has quit") End Sub 

All of the registered handlers will get invoked when the event delegate is called. You may unregister a handler at any time with the RemoveHandler keyword.

Chat Room Complete Example (Step 1)

The chat room example in EventDemo\Step1 illustrates the complete architecture on both the server and client sides. The server provides the following methods:

  • JoinChat

  • QuitChat

  • ShowMembers

Whenever a new member joins or quits, the server sends a notification to the client. The event handlers print out an appropriate message. Here is the output from running the program:

 graphics/codeexample.gif sender = OI Chat Room, Michael has joined the chat sender = OI Chat Room, Bob has joined the chat sender = OI Chat Room, Sam has joined the chat --- After 3 have joined--- Michael Bob Sam sender = OI Chat Room, Bob has quit the chat --- After 1 has quit--- Michael Sam 
Client Code

The client program provides event handlers. It instantiates a server object and then hooks up its event handlers to the events. The client then calls methods on the server. These calls will trigger the server, firing events back to the client, which get handled by the event handlers.

 ' ChatClient.vb Imports System Module ChatClient    Public Sub OnJoinChat(_     ByVal sender As Object, _     ByVal e As ChatEventArg)       Console.WriteLine(_          "sender = {0}, {1} has joined the chat", _          sender, _          e.m_name)    End Sub    Public Sub OnQuitChat(_     ByVal sender As Object, _     ByVal e As ChatEventArg)       Console.WriteLine(_          "sender = {0}, {1} has quit the chat", _          sender, e.m_name)    End Sub    Sub Main()       Dim chat As ChatServer = _          New ChatServer("OI Chat Room")       ' Register to receive event notifications       AddHandler chat.Join, AddressOf OnJoinChat       AddHandler chat.Quit, AddressOf OnQuitChat       ' Call methods on the server       chat.JoinChat("Michael")       chat.JoinChat("Bob")       chat.JoinChat("Sam")       chat.ShowMembers("After 3 have joined")       chat.QuitChat("Bob")       chat.ShowMembers("After 1 has quit")    End Sub End Module 
Server Code

The server provides code to store in a collection the names of people who have joined the chat. When a person quits the chat, the name is removed from the collection. Joining and quitting the chat triggers firing an event back to the client. The server also contains the "plumbing" code for setting up the events, including declaration of the delegates, the events, and the event arguments. There are also helper methods for firing the events.

 ' ChatServer.vb Imports System Imports System.Collections Public Class ChatEventArg    Inherits EventArgs    Public m_name As String    Public Sub New(ByVal name As String)       m_name = name    End Sub End Class Public Delegate Sub JoinHandler(_  ByVal sender As Object, ByVal e As ChatEventArg) Public Delegate Sub QuitHandler(_  ByVal sender As Object, ByVal e As ChatEventArg) Public Class ChatServer    Private m_members As ArrayList = New ArrayList()    Private m_chatName As String    Public Event Join As JoinHandler    Public Event Quit As QuitHandler    Public Sub New(ByVal chatName As String)       Me.m_chatName = chatName    End Sub    Public Overrides Function ToString() As String       Return m_chatName    End Function    Protected Sub OnJoin(ByVal e As ChatEventArg)       RaiseEvent Join(Me, e)    End Sub    Protected Sub OnQuit(ByVal e As ChatEventArg)       RaiseEvent Quit(Me, e)    End Sub    Public Sub JoinChat(ByVal name As String)       m_members.Add(name)       OnJoin(New ChatEventArg(name)) 'fire the Join event    End Sub    Public Sub QuitChat(ByVal name As String)       m_members.Remove(name)       OnQuit(New ChatEventArg(name)) 'fire the Quit event    End Sub    Public Sub ShowMembers(ByVal msg As String)       Console.WriteLine("--- " & msg & "---")       Dim member As String       For Each member In m_members          Console.WriteLine(member)       Next    End Sub End Class 

It may appear that there is a fair amount of such plumbing code, but it is much simpler than the previous connection-point mechanism used by COM for events. Also, most of the plumbing is on the server side, not the client side. It is quite easy to write programs that handle events. Static event handling makes the job even easier.

Static Event Handling

Static event handling in VB.NET is simpler but less flexible. If a class implements events, you may declare an object reference to that class using the WithEvents keyword. The VB.NET compiler will then look for matching procedures with the Handles keyword and, under the hood, instantiate delegates and add handlers.

As an example, consider EventDemo\Step2 . The server program is unchanged. The client program now uses static event handling.

 ' ChatClient.vb ' Step 2 - Static Event Handling Imports System Module ChatClient  Dim WithEvents chat As ChatServer  Public Sub OnJoinChat(_     ByVal sender As Object, _     ByVal e As ChatEventArg)  Handles chat.Join  Console.WriteLine(_          "sender = {0}, {1} has joined the chat", _          sender, e.m_name)    End Sub    Public Sub OnQuitChat(_     ByVal sender As Object, _     ByVal e As ChatEventArg)  Handles chat.Quit  Console.WriteLine(_          "sender = {0}, {1} has quit the chat", _          sender, e.m_name)    End Sub    Sub Main()       Dim chat As ChatServer = _          New ChatServer("OI Chat Room")       ' Don't need these AddHandler calls       ' AddHandler chat.Join, AddressOf OnJoinChat       ' AddHandler chat.Quit, AddressOf OnQuitChat       ' Call methods on the server       chat.JoinChat("Michael")       chat.JoinChat("Bob")       chat.JoinChat("Sam")       chat.ShowMembers("After 3 have joined")       chat.QuitChat("Bob")       chat.ShowMembers("After 1 has quit")    End Sub End Module 

Team-Fly    
Top
 


Application Development Using Visual BasicR and .NET
Application Development Using Visual BasicR and .NET
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 190

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net