Visual Basic is an event-driven language. This is especially true of programs written to run on the Windows desktop. After some important initialization, the user is generally in control of all actions in the program. Who knows what the crazy user will do? He might click here. She might type there. It could be all mayhem and bedlam. But whatever the user does, your program will learn about it through events.
Since the first days of Windows, desktop programs have used a message pump to communicate user and system actions to your code. Mouse and keyboard input, system-generated actions, and other notifications from external sources flow into a program's common message queue. The message pump draws these messages out one by one, examines them, and feeds them to the appropriate areas of your code.
In traditional Windows programming, you craft the message pump yourself, including code that makes direct calls to event-handling procedures based on the message type. In a Visual Basic program (both in .NET and earlier), the language provides the message pump for you. It analyzes the messages as they are pumped out of the message queue, and directs them to the appropriate code. In .NET, this code appears within classes. Once a class has a chance to analyze the message, it can generate an event, which is ultimately processed by an event handler, a subroutine you write to respond to the action. This calling of the event handler is know as firing an event. So there are two parts of an event: (1) some code that decides to fire the event, and (2) an event handler that responds to the event.
Events are really just indirect calls to a procedure. Instead of having the main code call another subroutine directly, it asks .NET to call the other subroutine for it, passing specific arguments that the original code may wish to include. So why would I want to do this instead of just making the subroutine call directly? For one thing, this indirect method lets you add event handlers long after the initial event-firing code was written. This is good, because the event-firing code may be in a third-party assembly that was written years ago. A second benefit is that one event can target multiple event handlers. When the event fires, each of the event handlers will be called, and each can perform any custom logic found in the handler subroutine.
The code that fires the event passes event-specific data to the target event handler(s) through the handlers' parameter list. In order for the indirect subroutine call to work, the event handler needs to contain the correct number of arguments, in the right order, each of a specific and expected data type. The Event statement defines this contract between the event and the handler.
Public Event SalaryChanged(ByVal NewSalary As Decimal)
This Event statement defines an event named SalaryChanged with a single argument, a Decimal value. Any event handler wishing to monitor the event must match this argument signature.
Sub EmployeePayChanged(ByVal updatedSalary As Decimal) . . .
Events can occur for any reason you deem necessary; they need not be tied to user or system actions. In this sample class, an event fires each time a change is made to the employee's salary. The RaiseEvent statement performs the actual firing of the event, specifying the name of the event to fire, and a set of arguments in parentheses.
Public Class Employee Public Name As String Private currentSalary As Decimal Public Property Salary() As Decimal Get Return currentSalary End Get Set(ByVal value As Decimal) currentSalary = value RaiseEvent SalaryChanged(currentSalary) End Set End Property Public Event SalaryChanged(ByVal NewSalary As Decimal) End Class
The event handlers are not added directly to the class. Instead, they are attached to an instance of the class. The instance, declared as a class field, must be defined using the special WithEvents keyword, which tells Visual Basic that this instance will process events.
Public WithEvents MonitoredEmployee As Employee
Event handlers are ordinary subroutines, but they include the Handles keyword to indicate which event is being handled.
Private Sub EmployeePayChanged( _ ByVal updatedSalary As Decimal) _ Handles MonitoredEmployee.SalaryChanged MsgBox("The new salary for " & _ MonitoredEmployee.Name & " is " & updatedSalary) End Sub
All that is needed is something to kick off the action.
Public Sub HireFred() monitoredEmployee = New Employee monitoredEmployee.Name = "Fred" monitoredEmployee.Salary = 50000 ' Triggers event End Sub
When the salary is set, the Employee class' Salary property raises the SalaryChanged event using the Visual Basic RaiseEvent command. This fires the EmployeePayChanged event handler, which finally displays the message.
The events built into the Windows Forms classes in .NET work just like this, but instead of watching with me for a salary increase, they are watching for mouse clicks and keyboard clacks. All of these system events use a common argument signature.
Event EventName(ByVal sender As System.Object, _ ByVal e As System.EventArgs)
The sender argument identifies the instance of the object that is firing the event, in case the caller needs to examine its members. The e argument is an object that lets the caller send event-specific data to the handler through a single class instance. The System.EventArgs class doesn't have much in the way of members, but many events use a substitute class that is derived from System.EventArgs.
As we pass through the chapters of this book, there will be no end to the number of event examples you will see and experience. I will save the more involved and interesting samples until then.