Event handling on ArcGIS controls


Summary
Events occur any time there is interaction with an application during its runtime. When a new map is loaded, when the mouse moves inside the control window, or when a keyboard button is pressed are examples of when events occur, and at such times the information regarding the event is reported. If the application is listening for events, it will execute code associated with the event that has occurred.

To make an application that responds to events on the controls without having a tool or command selected, you will not only need to tell the application what to do when such events are received, but will also have to tell the computer to be listening for and reacting to such events. This document details how to listen for the events, but does not go into detail on how to write your event class. For help on writing an event class, see Writing a MapControl Event class for Motif and Writing a Transform Event class for Motif.
If, however, you want to use a tool or a command, you will not need to listen for events. Custom tools and commands interact with the controls through their own functions, which are called upon similar events. See Creating a custom command for Motif in C++ or Creating a custom tool for Motif in C++ for more details.
Although there are also Motif Events, interacting with ArcObjects (such as popup menus) through those events is not recommended. Instead, you should interact through the ArcObjects event interfaces.

Events on the controls

To handle control events you will need to write a class that inherits from the events with which you want to interact. For the main events on a control, which implement IDispatch, your C++ API event classes will inherit from a helper interface. Listening for these events is then simplified. The events this applies to are listed in the table below. For all other event types, you will implement a class that inherits from the ArcObjects interface with the events you want to listen for. Then you will listen for the events as outlined under "Other ArcObjects Events—Implemented with custom interfaces" below.
Once you have implemented your class, you will need to have your application listen for the events. To do so, you will use IEventHelper, which passes information between your application and the events, handling reference counting for you.

Initiating the listening process

Once you have written your event class, you will need to set up your application to listen for the events. This is a two-step process:
  1. IEventListenerHelper->Startup: sets up a link between the sink (your object, such as a MapControl) and the listener helper object.

    This step will need to be done before realizing the top-level widget and entering the main application loop.
  2. IEventListenerHelper->AdviseEvents: sets up a link between the listener helper object and the source of the events (for example, IActiveViewEvents). Since the main events on the controls are implemented using IDispatch, you will pass in NULL for the interface to listen to, as shown in the example below.

    Where this step is taken will depend on the type of object on which you want to listen. If you are listening on an object that does not change (for example, a MapControl) you can do this right after you call Startup on the events. If, however, you wish to listen to an object that will change during a single run of the application, such as the ActiveView, you will want to make this call each time that object changes. For example, to listen to events on a MapControl's ActiveView, you would want to make this call in the MapControl's OnMapReplaced event. This does mean that you would also need to implement the MapControl's main events.

Ending the listening process

For your application to exit cleanly, you will need to stop your application from listening for events before you shut it down. This is again a two-step process:
  1. IEventListenerHelper->Shutdown: removes the link between the sink (your object, such as a MapControl) and the listener helper object.
  2. IEventListenerHelper->UnadviseEvents: removes the link between the listener helper object and the source of the events (for example, IActiveViewEvents).
Since these steps will only need to be taken when the application is closed, both calls will be made from the Motif callback that listens for the application to be closed, before IAoInitialize's shutdown is called and before the application is uninitialized.

Main control events — Implemented with IDispatch

To handle the main events of the controls, write a class that inherits from a helper interface that implements the events you are going to listen for. For example, to listen for IPageLayoutControlEvents, write a class that inherits from IPageLayoutControlEventsHelper. For further implementation details, see the walkthrough Writing a MapControl event class for Motif. You will then listen for the events by using the helper interface IEventListenerHelper.

Include files

To use these events, you will need to include the following files in your event class's header file:
  • esrisystemutility.tlh
  • <ControlName>control.tlh
  • <ControlName>control_events.tlh
For example, to listen to MapControl events, you would have the following include directives:
[C++]
#include <esrisystemutility.tlh>
#include <mapcontrol.tlh>
#include <mapcontrol_events.tlh>

Implementation details

To initiate listening for control-specific events, create an instance of your event class. Then create a new instance of IEventListenerHelper with a listener class ID that matches your event type. Call IEventListenerHelper->Startup then IEventListenerHelper->AdviseEvents. Notice that NULL is passed into the AdviseEvents call because a dispatch interface is being used. Your application is now set up to listen for the events you have implemented in your event class.

Before shutting down the application, you will need to call IEventsListenerHelper->UnadviseEvents, then IEventsListenerHelper->Shutdown. Remember to delete any associated global variables, such as the instance of your class, and to set any global interface pointers to 0.
Control
Inherit from
Event Listener class ID
GlobeControl
IGlobeControlEventHelper
CLSID_GlobeControlEventsListener
MapControl
IMapControlEvents2Helper
CLSID_MapControlEvents2Listener
PageLayoutControl
IPageLayoutControlEventsHelper
CLSID_PageLayoutControlEventsListener
ReaderControl
IARControlEventsHelper
CLSID_ARControlEventsListener
SceneControl
ISceneControlEventsHelper
CLSID_SceneControlEventsListener
TOCControl (table of contents)
ITOCControlEventsHelper
CLSID_TOCControlEventsListener
ToolbarControl
IToolbarControlEventsHelper
CLSID_ToolbarControlEventsListener
See the following code example:
[C++]
// Set up event listening
//   PageLayoutControlEvents is a custom class that inherits from IPageLayoutControlEventsHelper
//   g_pageLayoutEvents is a global PageLayoutControlEvents*
//   g_ipPageLayoutControlEventHelper is a global IEventListenerHelperPtr
//   g_ipPageLayoutControl is a global IPageLayoutControlPtr
g_pageLayoutEvents = new PageLayoutControlEvents();
g_ipPageLayoutControlEventHelper.CreateInstance
  (CLSID_PageLayoutControlEventsListener);
g_ipPageLayoutControlEventHelper->Startup(static_cast <
  IPageLayoutControlEventsHelper * > (g_pageLayoutEvents));
g_ipPageLayoutControlEventHelper->AdviseEvents(g_ipPageLayoutControl, NULL);

// Cleanup event listening
// You will need to clean up the events. This is done when the application is given the signal to close.
g_ipPageLayoutControlEventHelper->UnadviseEvents();
g_ipPageLayoutControlEventHelper->Shutdown();
g_ipPageLayoutControlEventHelper = 0;
delete g_pageLayoutEvents;
// Shutdown application with IAoInitialize's Shutdown

Other ArcObjects Events — Implemented with custom interfaces

To listen for the other events that the controls send, write a class that inherits from the ArcObjects interface that implements the events with which you want to interact. For example, to listen for ICustomizeDialogEvents, write a class that inherits from that interface.
For further implementation details, see the walkthrough Writing a Transform Event class for Motif.

Include files

To use these events, you will need to include the following file in your event class's header file:
  • esrisystemutility.tlh

Implementation details

To initiate listening for these events, you first need to get the interface on which to Advise (in the example below, IActiveView). Create an instance of your event class. Call both IEventListenerHelper->Startup and IEventListenerHelper->AdviseEvents. For these events, you will need to inform AdviseEvents of the UID of the type of events to listen for, as these events use custom interfaces and not a dispatch implementation.
Before shutting down the application, you will need to call IEventsListenerHelper->UnadviseEvents then IEventsListenerHelper->Shutdown. Remember to delete any associated global variables, such as the instance of your class, and to set any global interface pointers to 0.
  • The listener class IDs of the other events will be in the form:

    CLSID_<Coclass>Listener

    For example, to listen for ActiveViewEvents, use CLSID_ActiveViewEventsListener as the class ID.
  • Inherit from the ArcObjects interface that implements the events—IActiveViewEvents, for example.
See the following code example:
[C++]
// Set up event listening
// ActiveViewEvents is a custom class that inherits from IActiveViewEvents
// g_activeviewEvents is a global ActiveViewEvents*
// g_ipActViewEventHelper is a global IEventListenerHelperPtr
// g_ipMapControl is a global IMapControl2Ptr
g_activeviewEvents = new ActiveViewEvents();
g_ipActViewEventHelper.CreateInstance(CLSID_ActiveViewEventsListener);
g_ipActViewEventHelper->Startup(static_cast < ActiveViewEvents * > 
  (g_activeviewEvents));
CComBSTR bsGUID;
::StringFromIID(IID_IActiveViewEvents, &bsGUID);
IUIDPtr ipUID(CLSID_UID);
ipUID->put_Value(CComVariant(bsGUID));
IActiveViewPtr ipActiveView;
g_ipMapControl->get_ActiveView(&ipActiveView);
g_ipActViewEventHelper->AdviseEvents(ipActiveView, ipUID);

// Clean up event listening
// You will need to clean up the events. This is done when the application is given the signal to close.
g_ipActViewEventHelper->UnadviseEvents();
g_ipActViewEventHelper->Shutdown();
g_ipActViewEventHelper = 0;
delete activeviewEvents;
// Shut down application with IAoInitialize's Shutdown