Stingray® Foundation : Chapter 6 Events Package : Event Routers
Event Routers
An event router is an object that generates events and routes them to event listeners. Event listeners can be added and removed from an event router, and it is the event router’s responsibility to route the events to interested listeners. Event routers implement the IEventRouter interface, which contains three methods. Example 35 shows the IEventRouter interface.
Example 35 – IEventRouter interface
class __declspec(uuid("47E1CE36-D500-11d2-8CAB-0010A4F36466"))
IEventRouter : public IRefCount, public IQueryGuid
{
public:
/* Routes event objects to event listeners. */
virtual bool RouteEvent(IEvent* pIEvent) = 0;
/* Add an event listener to the router. */
virtual bool AddListener(IEventListener* pIListener) = 0;
/* Remove an event listener from the router. */
virtual bool RemoveListener(IEventListener* pIListener) = 0;
};
 
Default Event Router Implementation
The IEventRouterImpl class provides a default implementation of the IEventRouter interface. Example 36 shows the IEventRouterImpl implementation of RouteEvent().
Example 36 – IEventRouterImpl implementation of RouteEvent()
virtual bool RouteEvent(IEvent* pIEvent)
{
int nHandledCount = 0;
 
if (pIEvent != NULL)
{
ListenerVector::const_iterator itListener;
 
//
// Give each event listener a chance to handle the event.
//
for (itListener = m_listeners.begin();
itListener != m_listeners.end();
itListener++)
{
if ((*itListener)->HandleEvent(pIEvent))
{
nHandledCount++;
}
}
}
 
return (nHandledCount > 0);
}
 
The IEventRouterImpl class gives each event listener an opportunity to handle the event. An alternative implementation might stop as soon as a listener is found to handle the event.
ATL Integration
The Events package integrates seamlessly with the ATL message map architecture by generating and routing events processed by ATL message maps. ATL defines the CMessageMap class, which defines the ProcessWindowMessage() function for handling messages. The ATL message map macros implement the ProcessWindowMessage() function. The CEventRouterMap class derives from CMessageMap and implements the ProcessWindowMessage() function by creating an event using an event factory and then passing it to RouteEvent(). The implementation of CEventRouterMap is straightforward, as shown in Example 37.
Example 37 – CEventRouterMap implementation
template <typename T>
class CEventRouterMap : public CMessageMap
{
public:
virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam,
LRESULT& lResult,
DWORD dwMsgMapID = 0)
{
bool bHandled = FALSE;
T* pT = static_cast<T*>(this);
 
IEvent* pIEvent =
GetEventFactory()->CreateWindowsEvent(uMsg,
wParam,
lParam);
if (pIEvent != NULL)
{
bHandled = pT->RouteEvent(pIEvent);
pIEvent->Release();
}
 
return bHandled;
}
 
virtual CEventFactory* GetEventFactory()
{
static CEventFactory eventFactory;
return &eventFactory;
}
};
 
The CEventRouterMap class acts as a bridge between ATL message maps and the event-listener architecture. The template parameter passed into CEventRouterMap is the derived class, which is assumed to be an event router. The CEventRouterMap class defines a virtual GetEventFactory() method to provide derived classes the opportunity to supply a different event factory, which is useful for filtering events.
To use CEventRouterMap, you need to insert one line of code in your ATL message map to chain to the CEventRouterMap object. Example 38 shows an ATL window class that implements event routing from an ATL message map.
Example 38 – Implementing event routing from an ATL message map
class CMyWnd : public CWindowImpl<CMyWnd>,
public IEventRouterImpl,
public CEventRouterMap<CMyWnd>
{
public:
BEGIN_MSG_MAP(CMyWnd)
CHAIN_MSG_MAP(CWindowImpl<CMyWnd>)
CHAIN_MSG_MAP(CEventRouterMap<CMyWnd>)
END_MSG_MAP()
};
To integrate event routing with the message map, you need to derive from the CEventRouterMap class and then add one entry to the message map to chain to it. Alternatively, you can declare the CEventRouterMap as a member variable of your class and then use ATL’s CHAIN_MSG_MAP_MEMBER macro instead of CHAIN_MSG_MAP. Chaining to a CEventRouterMap does not interfere with the normal behavior of the ATL message map. In other words, the ATL message and event router logic live together happily.
MFC Integration
The Events package integrates seamlessly with the MFC message map architecture by hooking the OnWndMsg() and OnCmdMsg() functions and routing events for the Windows messages received by those two functions. The template class CMFCEventRouter wraps any CWnd derived class and overrides the OnWndMsg() and OnCmdMsg() functions to implement event creation and routing. The CMFCEventRouter class takes two template arguments. The first template argument is the derived class, which is assumed to be an event router. The CMFCEventRouter class does a static_cast to the derived class to invoke the RouteEvent() method. The second template parameter is the base class, which must be a CWnd derived class or any other class that defines OnWndMsg() and OnCmdMsg() with the same signature as CWnd. Example 39 shows the implementation of CMFCEventRouter.
Example 39 – CMFCEventRouter implementation
template <typename router, typename wndbase>
class CMFCEventRouter : public wndbase
{
public:
virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
BOOL bHandled = FALSE;
// Create the event and route it to event listeners.
CEventFactory* pEventFactory = GetEventFactory();
IEvent* pIEvent = NULL;
 
if (pHandlerInfo != NULL)
{
// This message is a request for handler info.
pIEvent=pEventFactory->CreateCommandQueryEvent(nID);
}
else if (nCode == CN_UPDATE_COMMAND_UI)
{
// Create a command update UI event.
CCmdUI* pCmdUI = (CCmdUI*) pExtra;
pCmdUI;
}
else
{
// Regular command event.
pIEvent =
pEventFactory->CreateCommandEvent(nID, nCode);
}
 
if (pIEvent != NULL)
{
// Route event to event listeners.
router* pT = static_cast<router*>(this);
bHandled = pT->RouteEvent(pIEvent);
pIEvent->Release();
}
 
return bHandled;
}
 
virtual BOOL OnWndMsg(UINT message, WPARAM wParam,
LPARAM lParam, LRESULT* pResult)
{
BOOL bHandled = FALSE;
// Create an event, using the event factory and
// route it to the event listeners.
CEventFactory* pEventFactory = GetEventFactory();
IEvent* pIEvent =
pEventFactory->CreateWindowsEvent(message,
wParam, lParam);
if (pIEvent != NULL)
{
router* pT = static_cast<router*>(this);
bHandled = pT->RouteEvent(pIEvent);
pIEvent->Release();
}
return bHandled;
}
 
virtual CEventFactory* GetEventFactory()
{
static CEventFactory eventFactory;
return &eventFactory;
}
};
 
The CMFCEventRouter class acts as a bridge between MFC message maps and the event-listener architecture. To use the CMFCEventRouter class, you need to mix it into your CWnd derived class. The following code is of an MFC window class that implements event routing.
 
class CMyWnd : public CMFCEventRouter<CMyWnd, CWnd>,
public IEventRouterImpl
{
};
Using the CMFCEventRouter class does not interfere with MFC’s own message map processing. Both techniques can co-exist without any conflicts and you can use MFC message maps in conjunction with event listeners to handle events.