Introducing Microsoft .NET (Pro-Developer)
|
Suppose we want to do more complex things than in the previous example, such as add or remove event handlers dynamically as our code executes rather than statically through a designer? And what about the case in which an event has more than one handler function, or a single handler function listens to more than one event? All of these scenarios are possible and, in fact, quite easy to implement, using the generic event mechanism that we saw in the previous example. I’ve written a sample program that demonstrates each of these cases. The program, shown in Figure 8-6, uses the component from the previous example for one demonstration and standard Windows Forms controls for the others.
A more complex events and delegates sample starts here.
Suppose I create an object dynamically while a program is running rather than statically in a designer. How could I hook up handler functions to the events fired by that object? It turns out to be quite simple. As before, I need a handler function that matches the proper signature, but in this case the handler doesn’t use the Handles keyword shown in the previous static example. Instead, in Visual Basic .NET, I use the run-time keyword AddHandler, as shown in Listing 8-5, to connect my handler function to the specified event on the object.
Listing 8-5: Visual Basic code for adding and removing an event handler.
Dim EventFiringComponent As SimplestEventComponentVB.Class1EventVB Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click ’ Create new component that fires events. EventFiringComponent = New SimplestEventComponentVB.Class1EventVB ’ Add a handler function that gets called ’ when the event is received AddHandler EventFiringComponent.SomethingHappened, _ AddressOf Me.HandleSomethingHappenedEvent End Sub Private Sub Button3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button3.Click ’ Remove the handler for the event RemoveHandler EventFiringComponent.SomethingHappened, _ AddressOf Me.HandleSomethingHappenedEvent End Sub ‘ Event received from component. Signal user with message box Private Sub HandleSomethingHappenedEvent(ByVal WhenItHappened As DateTime) MessageBox.Show("SomethingHappened event received, time = " + _ WhenItHappened) End Sub
The AddressOf keyword tells Visual Basic to find the address of the handler function. This is exactly what the Handles keyword told Visual Basic to do in the previous static case, but now we’re writing the explicit code for it. Visual Basic actually creates a delegate of the proper type, places the handler function into the delegate, and adds the new delegate to the handler list in the object’s delegate. You can see this more clearly in the C# example, shown in Listing 8-6.
Listing 8-6: C# code for adding and removing an event handler.
private SimplestEventComponentCS.Class1EventCS EventFiringComponent ; private void button1_Click(object sender, System.EventArgs e) { // Create new component that fires events. EventFiringComponent = new SimplestEventComponentVB.Class1EventCS (); // Add a handler function that gets called // when the event is received EventFiringComponent.SomethingHappened += new SimplestEventComponentCS.Class1EventCS. SomethingHappenedEventHandler ( this.HandleSomethingHappenedEvent) ; } private void button3_Click(object sender, System.EventArgs e) { // Remove event handler EventFiringComponent.SomethingHappened -= new SimplestEventComponentCS.Class1EventCS. SomethingHappenedEventHandler ( this.HandleSomethingHappenedEvent) ; } // Event received from component. Signal user with message box private void HandleSomethingHappenedEvent(DateTime WhenItHappened) { MessageBox.Show("SomethingHappened event received, time = " + WhenItHappened) ; }
I create a new SomethingHappenedEventHandler delegate, passing it the address of the handler function (this.HandleSomethingHappenedEvent) in its constructor. This handler function becomes the target function of the new delegate. I then use the += operator to add this new delegate to the handler list in the object’s delegate. When the component signals the event, it calls the Invoke method on its own delegate, which looks at its list of handler delegates and uses the delegates’ Invoke methods to call their target functions. Removing an event handler involves the same operation in reverse, using RemoveHandler in Visual Basic and the -= operator in C#.
Adding and removing event handlers dynamically is easy.
This manual hookup of events lets me do some useful things. For example, it’s quite easy to make one handler function handle any number of events. I might want to have the same function handle the selection of a menu item by a user, the click of a toolbar button, and a keyboard shortcut, all signaling the same command choice. This would let all of my code for handling a single user choice live in one place, regardless of the various locations of the logic that triggers it. To demonstrate this, I’ve written the code in Listing 8-7, which uses the same handler function to respond to clicks on any of three radio buttons. In the initialization of my form, I manually add a handler to each radio button, specifying the same handler function for each. If you follow this design pattern for your own events, you probably want to have them pass a parameter to the handler that allows it to distinguish between various senders.
Listing 8-7: Adding a handler for various events.
Public Sub New() MyBase.New() ’This call is required by the Windows Form Designer. InitializeComponent() ’ Add a handler for each separate radio button ’ routing the Click event to the same handler function AddHandler RadioButton1.Click, _ AddressOf Me.HandleClickOnAnyRadioButton AddHandler RadioButton2.Click, _ AddressOf Me.HandleClickOnAnyRadioButton AddHandler RadioButton3.Click, _ AddressOf Me.HandleClickOnAnyRadioButton ’ Add three separate handler functions to the click ’ event of a single button AddHandler Button4.Click, AddressOf FirstHandlerOfClick AddHandler Button4.Click, AddressOf SecondHandlerOfClick AddHandler Button4.Click, AddressOf ThirdHandlerOfClick End Sub
Radio buttons use the System.EventHandler delegate for their event handlers, as do all Windows Forms controls. The first parameter of this delegate is the object that is sending the event. My handler code looks at this parameter to tell me which control the user has clicked on.
You can connect one handler function to multiple events.
You can also repeat the hookup operation (discussed previously) multiple times to hook up more than one handler function to a single event. The code in Listing 8-7 shows my sample program hooking up three separate handler functions to the Click event from a single button. This lets you perform more than one action in response to an event, without each handler needing to know about the others.
You can also connect multiple handler functions to a single event.
My customers report that they find themselves using the former operation—having the same handler function take care of more than one event—much more often than the latter. There’s nothing wrong with the second approach, it just doesn’t seem to arise nearly as often for them.
|