Simplest Example


As I’ve done throughout this book, I wrote the simplest program I could imagine to demonstrate the use of delegates and events. The code itself is quite easy to write, but the hoops through which the .NET Framework jumps to make it all happen are quite complex. We can now examine and understand the event system without any business logic getting in the way.

A simple event and delegate example begins here.

I wrote a simple .NET class library containing an object that signals an event to its client. Unlike most parts of .NET, the syntax differs significantly between Visual Basic .NET and C#, so I’ll show both languages in this text. Listing 8-1 shows the Visual Basic code, and Listing 8-2 shows the C# code for my object. The object provides one event, called SomethingHappened. When the component is created, I start a timer (for which I don’t show the code here; look at the online samples if you care to) to make the component signal its event every 5 seconds without any input from the client.

Listing 8-1: Visual Basic code for the simplest event component.

start example
Public Class Class1EventVB Inherits System.ComponentModel.Component ’ Start a background timer that ticks every 5 seconds      (code omitted) ’ Declare an event that this component can fire to its container. Public Event SomethingHappened(ByVal WhenItHappened As DateTime) ’ When the timer expires, fire the event to any interested listeners Public Sub TimerExpired(ByVal state As Object) ’ Use the keyword RaiseEvent to signal the event mechanism ’ to call the handler functions in each listener, if any. RaiseEvent SomethingHappened(Now) End Sub End Class
end example

Listing 8-2: C# code for simplest event.

start example
public class Class1EventCS : System.ComponentModel.Component { // Start a background timer that ticks every 5 seconds (code omitted) // Declare the delegate, which is a function signature // that a listener must implement public delegate void SomethingHappenedEventHandler ( DateTime WhenItHappened) ; // Declare an event, which is something our component can // notify its container of, specifying the declared delegate // that a listener must implement to hear about it. public event SomethingHappenedEventHandler SomethingHappened ; // When the timer expires, fire the event to any // interested listeners Public Sub TimerExpired(ByVal state As Object) { // Invoke the event directly by means of its name. // This causes the event mechanism to call the // handler functions in each listener, if any.          SomethingHappened (DateTime.Now) ; } }
end example

First you’ll see that both objects inherit from the system base class System.ComponentModel.Component. This inheritance is not required for firing events. I’ve added it to this example because, as you’ll see a little later when we start implementing the listener, by deriving from this class I can load the component into the Visual Studio designer and use the wizards to add handlers for the events to my sample client program. You could omit this inheritance and your events would still work fine; they’d just be a little harder for clients to program. In Visual Basic .NET, I use the keyword Event to declare an event, specifying its name, in this case SomethingHappened. I also provide a list of parameters that I want the event to pass to its listener, in this case a single DateTime object specifying the time at which the event took place. In C# syntax, I use the keyword delegate. This keyword declares a delegate with the name SomethingHappenedEventHandler that wraps a target function having the specified parameter list. In the next line, I use the keyword event to say that the event named SomethingHappened requires a delegate of the type SomethingHappenedEventHandler. I can, and often will, have many events with different names that require the same type of delegate. All events from Windows Forms controls, for example, derive from the same generic delegate class System.EventHandler. In my Visual Basic .NET example, the compiler implied the delegate declaration from the Event statement, although Visual Basic .NET also supports syntax similar to C# syntax if you want to use the same type of delegate for many different events. But in either language, I have to write only one or two lines of code to create an event that listeners can connect to and that my component can signal. Now let’s look at the prefabricated event support that .NET has built into the component.

Declaring an event is extremely simple.

I used ILDASM (the intermediate language disassembler; see Chapter 2) to produce the disassembly of my Visual Basic component, shown in Figure 8-3. Despite the different source code syntax, the ILDASM of the Visual Basic and C# components are nearly identical, so I’ll show only one.

click to expand
Figure 8-3: Disassembly view of the class that fires the event.

Looking at the bottom line of the window shown in Figure 8-3, we see a downward-pointing triangle (colored green when you look at it on your PC). This line is extracted from metadata that says that this component fires an event named SomethingHappened. Scanning along the same line, we see a reference to an object of class SomethingHappenedEventHandler. This object is the delegate that a listener must provide to this sender if the listener wants to receive notifications of this event. Any client, such as a development environment that provides wizard support for writing event handlers, that wants to see the events that this class signals and the handlers those events require can find the description in that metadata.

OK, what is this mysterious delegate called SomethingHappenedEventHandler? Looking again at Figure 8-3, we can see the delegate nested inside our component class, and we can see that it is listed as extending (inheriting, or deriving from) System.MulticastDelegate, which in turn derives from System.Delegate. A MulticastDelegate object can hold more than one target function pointer, thereby allowing a sender to signal an event to multiple listeners with a single function call. The private variable marked with a diamond, called SomethingHappenedEvent, is an instance of the SomethingHappenedEventHandler class. SomethingHappenedEvent is the actual object that our sending object will use to signal the event. Since SomethingHappenedEvent is private, a listener can’t talk to it directly. Instead, we can see public methods on our main object called add_SomethingHappened and remove_SomethingHappened. A listener will call these methods to connect to the event. You won’t actually write these functions in your class; your language compiler will add them for you while you write more abstract statements, as we will see later in this chapter. The compiler did something similar when you wrote code that accessed an object property, exposed by means of hidden set and get methods, which you can see with ILDASM if you ever care to look for them.

The compiler also generates a delegate class to hold the event listeners’ callback functions.

Now that we’ve seen the simple syntax we need to write an object that generates an event, and the much larger amount of internal support that .NET generates for us as a result, we next wonder what kind of code the developer of a listener needs to write to be a recipient of an event notification. In this case I’ve written the event-sending components as deriving from the system base class System.ComponentModel.Component, as we saw in Listings 8-1 and 8-2. This means that I can load the components into Visual Studio .NET and use its designers and wizards to make writing a listener easier. First I generate a Windows Forms application (see Chapter 5) to provide a user interface for my sample listener program. Then I add the sending component (SimplestEventComponentxx.dll) to my toolbox by right-clicking in the Components section of the toolbox, selecting Customize Toolbox, selecting the .NET Framework Components tab, and browsing to locate that component. The component will appear in the toolbox (as Class1Eventxx). When I drag it onto my client form, it will appear in the design window below the form, as shown in Figure 8-4, because it doesn’t have its own user interface.

I place the component on my project using the toolbox.

click to expand
Figure 8-4: Visual Studio, placing a new component on a form.

The Visual Basic .NET code for the listening form class is shown in Listing 8-3. You’ll find when you look at the Windows Forms Designer–generated code that Visual Studio has added a member variable for our component declared with the keyword WithEvents. This keyword tells the compiler that the component is a potential sender of events of the specified type to interested listeners.

Listing 8-3: Visual Basic .NET code for handling events.

start example
Public Class Form1 Inherits System.Windows.Forms.Form Friend WithEvents Class1EventVB1 As _ SimplestEventComponentVB.Class1EventVB ’ This handler function gets invoked when the ’ "SomethingHappened" event comes in. Private Sub Class1EventVB1_SomethingHappened(ByVal WhenItHappened _ As Date) Handles Class1EventVB1.SomethingHappened MessageBox.Show("SomethingHappened event received, fired at " + _ WhenItHappened.ToLongTimeString) End Sub End Class
end example

When I drop down the Class Name list box in the Visual Basic Code Editor, I’ll see this variable. When I select it, I can drop down the Method Name list box and see my sample event SomethingHappened. Visual Basic .NET knew to populate the Method Name list box with the methods and events associated with my chosen component name by reading the component’s metadata, which I showed you in the viewer in Figure 8-3. When I select this event, Visual Studio .NET adds the event handler function to the form (listener) class. The event handler implements the signature required to receive the event notification from the sending object. The modifier Handles Class1EventVB1.SomethingHappened tells the Visual Basic compiler to generate the code that creates a delegate with this function as its target and that calls add_SomethingHappened on the sending object to connect this delegate to the object’s delegate that represents the event.

Visual Basic provides an easy way to add an event handler function to a form.

C# provides a slightly different way of connecting a handler to an event. To create this project in C#, I generate the Windows Forms project and add the component to the project just as I did in Visual Basic. Events don’t appear in a drop-down list, as they do in Visual Basic. Instead, when I select the component on the designer window below the form and view its properties, the Properties window contains a little lightning bolt icon. When I click that, I’ll see a list of events in the Properties window, as shown in Figure 8-5. I type in the name of a handler function (HandleSomethingHappened), and C# adds it to the code with the proper signature, as shown in Listing 8-4. You don’t see the Handles notation as you did with Visual Basic. Instead, the event hookup code is performed in the InitializeComponent method where the rest of the properties are set. It uses the syntax I’ll discuss in the next example, where I’ll show you how to write it yourself in dynamic hookup cases that the wizard can’t handle.

Adding an event handler in C# is again different, but still simple.

click to expand
Figure 8-5: C# Properties window showing events fired by the sample component.

Listing 8-4: C# event handler code added by the wizard.

start example
// This handler function gets invoked when the // "SomethingHappened" event comes in. private void HandleSomethingHappened(System.DateTime WhenItHappened) { MessageBox.Show ("Event SomethingHappened received, fired at " + WhenItHappened.ToString()) ; } 
end example

When the timer ticks, it calls a function in my component in which I’ve place the code that fires the event. In Visual Basic, an event is fired through the keyword RaiseEvent. In C#, we simply call the event. Internally, in both cases firing the event calls the Invoke method on the component’s SomethingHappenedEvent object (the private delegate discussed earlier). This call causes the delegate to look through its list of listener delegates and call the target functions on every one. In this example, we have only one target function—the event handler on our form. In C#, signaling an event that has no listeners causes an exception, so you’ll have to add your own exception handler (see Chapter 2) if you think this case will ever occur. Alternatively, the event variable itself will be null if no listener has registered a handler, which you could check before calling. In Visual Basic, the RaiseEvent keyword contains its own internal logic for this case, so you don’t have to write it. (It feels like there ought to be a tree-falling-in-the-forest joke here, but I can’t quite figure out what it is.)

A component fires an event with RaiseEvent in Visual Basic or by calling its name in C#.

start sidebar
Tips from the Trenches

The event mechanism is a good example of the old saying “easy is hard.” It’s very easy for an application programmer to use events. It’s difficult to write a compiler that provides this ease of use.

end sidebar




Introducing Microsoft. NET
Introducing Microsoft .NET (Pro-Developer)
ISBN: 0735619182
EAN: 2147483647
Year: 2003
Pages: 110

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