< Day Day Up > |
Hidden behind the event syntax described in this chapter is a fair amount of plumbing. Events are not as simple as they look ”underneath the covers, an event is really just a delegate. (This is why this chapter covers both events and delegates.) For example, the following code: Class Button Event Moved(ByVal X As Integer, ByVal Y As Integer) Sub Move(ByVal X As Integer, ByVal Y As Integer) RaiseEvent Moved(X, Y) End Sub End Class at an implementation level looks more like this. Class Button Public Delegate Sub ClickEventHandler() Private ClickEvent As ClickEventHandler Public Sub add_Click(ByVal NewHandler As ClickEventHandler) ClickEvent = CType(System.Delegate.Combine(ClickEvent, _ NewHandler), ClickEventHandler) End Sub Public Sub remove_Click(ByVal OldHandler As ClickEventHandler) ClickEvent = CType(System.Delegate.Remove(ClickEvent, OldHandler), _ ClickEventHandler) End Sub Public Sub Click() Dim TempClickDelegate As ClickEventHandler TempClickDelegate = ClickEvent If Not TempClickDelegate Is Nothing Then TempClickDelegate() End If End Sub End Class It's worthwhile going through this code step-by-step. The first step in declaring an event is to define a delegate that has the same signature as the event. When you are declaring an event, the compiler declares a nested delegate type named XEventHandler for an event named X . It is also possible to use a preexisting delegate type when you are declaring an event by using an alternate event syntax. Class Button Event Click As EventHandler End Class In this case, the event does not declare its own delegate type ”instead, it uses the System.EventHandler delegate type as its delegate type. This means it has the same signature as if the declaration had been as follows . Class Button Event Click(ByVal sender As Object, ByVal e As EventArgs) End Class Note that the delegate type used in the alternate syntax must be a subroutine ”declaring events with return types is not supported. The delegate is used to hold references to all the methods that are handling a particular event. When another class wants to handle the Click event, it creates a delegate that refers to its handler and then passes the delegate to the add_Click method. The add_Click method combines the passed-in delegate with the other delegates already handling the event, if any. When another class wants to stop handling the Click event, it creates another delegate that refers to its handler and then passes the delegate to the remove_Click method. The remove_Click method removes the delegate from the list of delegates handling the event. When a class raises an event, it must first check to make sure that the delegate is not Nothing . If the delegate is Nothing , no one is handling the event and no work needs to be done. If the delegate is not Nothing , the delegate is invoked with the provided parameters. This causes each handler to be called with the specified parameters.
This covers the event declaration side, but what of the event handling side? AddHandler and RemoveHandler turn into direct calls to the add and remove methods of the event. But declarative event handling turns out to be quite a bit more complex. For example, the following code: Class Form1 Public WithEvents Button1 As Button Public Sub Button1_Click() Handles Button1.Click MsgBox("Button1 was clicked!") End Sub End Class at an implementation level looks more like this. Class Form1 Private _Button1 As Button Public Property Button1() As Button Get Return _Button1 End Get Set (ByVal Value As Button) If Not _Button1 Is Nothing Then RemoveHandler _Button1.Click, AddressOf Button1_Click End If _Button1 = Value If Not _Button1 Is Nothing Then AddHandler _Button1.Click, AddressOf Button1_Click End If End Set End Property Public Sub Button1_Click() MsgBox("Button1 was clicked!") End Sub End Class At the implementation level, a WithEvents field is a property that knows how to hook up all the event handlers for that field. When a value is assigned to the property, the Set accessor first removes the event handlers from the existing delegate stored in the property. It then changes the value of the property and adds the event handlers to the new instance. Declaratively handling events in a base class is a little different because there is no field to hook up to. Instead, the instance that raises events is always the instance of the class itself. So the following code: Class Base Public Event Click(ByVal x As Integer, ByVal y As Integer) End Class Class Derived Inherits Base Public Sub Clicked(ByVal x As Integer, ByVal y As Integer) _ Handles MyBase.Click MsgBox("Derived was clicked!") End Sub End Class at an implementation level looks more like this. Class Derived Inherits Base Public Sub New() AddHandler Me.Click, AddressOf Me.Clicked End Sub Public Sub Clicked(ByVal x As Integer, ByVal y As Integer) MsgBox("Derived was clicked!") End Sub End Class When the class is constructed , all it needs to do is add a handler to the base event. It never needs to stop handling the event, because that will happen automatically when the class is finalized. |
< Day Day Up > |