Rogue Wave banner
Previous fileTop of DocumentContentsIndex pageNext file
Stingray Foundation Library User's Guide
Rogue Wave web site:  Home Page  |  Main Documentation Page

6.4 Event Listeners

Event listeners subscribe to event routers to receive events. The IEventListener interface defines a single method, HandleEvent(), as shown in Example 40.

Example 40: HandleEvent() defined in the IEventListener interface

An event listener can implement HandleEvent() any way it chooses, but the typical implementation invokes the event's Dispatch() method. Example 41 demonstrates a typical implementation of HandleEvent().

Example 41: Typical HandleEvent() implementation

6.4.1 Dispatching Events

Notice that the event listener passes a pointer to itself into the event object's Dispatch() method. The Dispatch() method queries the listener for an interface that it understands and then invokes the callback method on that interface. Example 42 demonstrates how the paint event class invokes the OnPaint() callback function.

Example 42: Invoking the OnPaint() callback function

The event's Dispatch() method checks to see if it has the right type of listener. If it does have the right listener, it invokes the appropriate callback method. In the preceding sample code, the event listener is expected to implement the IWindowListener interface to receive the OnPaint() callback.

Although HandleEvent() method is sufficient for handling all the events that a listener is interested in receiving, doing so would be equivalent to writing a window procedure containing a big switch() statement. The idea behind making event handling simpler is to map events onto individual member functions. For example, you could map a WM_PAINT message onto an OnPaint() member function that receives a device context as a parameter. Event listener interfaces extend the base IEventListener interface with callback functions that are invoked to handle events. An example of an event listener interface is IWindowListener, shown in Example 43.

Example 43: An event listener interface, IWindowListener

The HandleEvent() method inherited from IEventListener is called to handle all events, but it delegates the work to the callback functions by calling the event object's Dispatch() method. The flow of control for handling events is as follows:

The event object is passed to the event listener's HandleEvent() method. The HandleEvent() method delegates the task of invoking the correct callback function to the event object. Be aware that this is only the default behavior, and can easily be overridden. For example, you might handle certain events directly in your event listener's HandleEvent() method without invoking a callback function. It is also possible to implement HandleEvent() in such a way that it bypasses the event object's Dispatch() method and invokes the callback methods directly. The architecture is flexible enough to allow many different event listener implementations.

6.4.2 Adapter Classes

Implementing the HandleEvent() method along with each callback function every time you want to write an event listener is time-consuming. Adapter classes provide default implementations of event listener interfaces. In addition to implementing HandleEvent() and the event listener callback methods, adapters implement the QueryGuid(), AddRef(), and Release() methods inherited from the IQueryGuid and IRefCount interfaces. The CEventListenerBase class is a generic base class that you can use to implement adapters. It provides implementations of the HandleEvent(), QueryGuid(), AddRef(), and Release() methods. The CEventListenerBase implementation of HandleEvent() is the typical one described in Section 6.4, and is as follows:

The CEventListenerBase class is a template that takes the event listener interface as an argument, and then derives itself from the given event listener interface. The CWindowAdapter class uses the CEventListenerBase to provide a default implementation of the IWindowListener interface, as shown in Example 44.

Example 44: CWindowAdapter using CEventListenerBase to implement IWindowListener by default

The adapter class provides basic stub implementations of each callback function, so that developers don't have to implement every single callback function in their own event listener classes.

6.4.3 Using Event Listeners

Now let's implement a class that listens for events. Our example will be a class that paints the text "Hello World" in the center of a window. The first step is to mix in the CWindowAdapter class so that our class can listen for paint and size events.

Example 45: Adding CWindowAdapter to a class that will listen for events

The CHelloObject class overrides the OnSize() and OnPaint() event handler functions it inherits from IWindowEvent to paint the text "Hello World" in the center of a window. Now all we need is a window capable of routing events, in which to plug an instance of the CHelloObject class. The CHelloWnd class shown in Example 46 implements IEventRouter and generates events from the ATL message map using the CEventRouterMap class.

Example 46: CHelloWnd class implementing IEventRouter and generating events from the ATL message map by using the CEventRouterMap class

6.4.4 Efficiency of Event Listeners vs. Message Maps

Both MFC and ATL use message maps to avoid the overhead of using virtual functions for handling messages. For example, if MFC's CWnd class defined a virtual function for every possible message a window might receive, the v-table would be large. It would also mean that the interface to CWnd would have to change every time a new message type was added to the Windows API.

Event listeners define message handlers as virtual functions, but the monolithic v-table problem is avoided by partitioning event listeners into small interfaces. Rather than putting every event handler method into a single monolithic base class, you can mix in event listeners that are small interfaces as needed. Event listeners generally handle one event or a limited category of events. For example, the IMouseListener interface has eight virtual functions to handle every type of mouse event. If a class is only interested in mouse events, it does not have to pay the v-table overhead of having virtual functions for every other type of Windows message. Partitioning event listeners into categories and mixing them into classes as interfaces is ideal; it provides the convenience of using virtual methods for event handlers and avoids the overhead of large, monolithic v-tables.



Previous fileTop of DocumentContentsNo linkNext file

Copyright © Rogue Wave Software, Inc. All Rights Reserved.

The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.