How to write a MapControl event class


With ArcGIS Engine, you can listen for events on the ArcGIS controls such as the MapControl. For example, you can implement custom application responses to mouse clicks, keyboard input, extents being updated, and maps being replaced. For the MapControl, you will do this by writing a class that inherits from IMapControlEvents2Helper. For the default events on other controls, you will inherit from the helper interface for their default events:
Control
Helper interface for default events
GlobeControl
IGlobeControlEventsHelper
MapControl
IMapControlEvents2Helper
PageLayoutControl
IPageLayoutControlEventsHelper
ReaderControl
IARControlEventsHelper
SceneControl
ISceneControlEventsHelper
TOCControl (table of contents)
ITOCControlEventsHelper
ToolbarControl
IToolbarControlEventsHelper
This document will take you through writing an event class for a MapControl, which will cause a message to be printed to the terminal window each time a new map is loaded into the MapControl.
Control events should not be used in the place of a custom command or tool. For example, to write a custom zoom, you should write a custom tool. If instead you listen for mouse clicks on the control, you will always have to use your zoom with the mouse and will not be able to turn it off, as you can with a custom tool. For further details on custom commands and tools, see Creating a custom tool using AoToolBase and Creating a custom command using AoCommandBase.
Since events are used to interact with ArcGIS Controls, they only work on Solaris and Linux. If you wish to use your application on Windows, you will need to use a different API, such as Visual C++.

MapControl events example: New Map Loaded message box

When you load a new map into your MapControl, these events will simply display a message box informing you that a new map has been loaded.

Creating the MapControl Event class

  1. Create a new file in your text editor: MapControlEvents.h.
  2. In MapControlEvents.h, create a new class, MapControlEvents. You will need to include ArcSDK.h for the Engine. You will need to include Ao/AoControls.h for the MapControl events. For the message you will need to include iostream.
[Motif C++]
#ifndef __MAPCONTROLEVENTS2_H_
  #define __MAPCONTROLEVENTS2_H_

  #include <iostream>

  // ArcObjects Headers
  // Engine
  #include <ArcSDK.h>
  // Controls
  #include <Ao/AoControls.h>

  class MapControlEvents{}
  ;

#endif // __MAPCONTROLEVENTS2_H_
  1. Update the MapControlEvents class  just created so that it inherits from IMapControlEvents2Helper. Add the functions from IMapControlEvents2Helper, which you need to implement, as well as the definition for IUnknown.
[Motif C++]
class MapControlEvents: public IMapControlEvents2Helper
{
  public:
    // IUnknown  
    IUNKNOWN_METHOD_DEFS 

    // IMapControlEvents2
    void OnAfterDraw(VARIANT display, long viewDrawPhase);
    void OnAfterScreenDraw(long hdc);
    void OnBeforeScreenDraw(long hdc);
    void OnDoubleClick(long button, long shift, long x, long y, double mapX,
      double mapY);
    void OnExtentUpdated(VARIANT displayTransformation, VARIANT_BOOL
      sizeChanged, VARIANT newEnvelope);
    void OnFullExtentUpdated(VARIANT displayTransformation, VARIANT newEnvelope)
      ;
    void OnKeyDown(long keyCode, long shift);
    void OnKeyUp(long keyCode, long shift);
    void OnMapReplaced(VARIANT newMap);
    void OnMouseDown(long button, long shift, long x, long y, double mapX,
      double mapY);
    void OnMouseMove(long button, long shift, long x, long y, double mapX,
      double mapY);
    void OnMouseUp(long button, long shift, long x, long y, double mapX, double
      mapY);
    void OnOleDrop(esriControlsDropAction dropAction, VARIANT dataObjectHelper,
      long *effect, long button, long shift, long x, long y);
    void OnSelectionChanged();
    void OnViewRefreshed(VARIANT ActiveView, long viewDrawPhase, VARIANT
      layerOrElement, VARIANT envelope);
};

Adding code to the members of IMapControlEvents2Helper

  1. Create a new file in your text editor: MapControlEvents.cpp.
  2. Include MapControlEvents.h.
[Motif C++]
#include "MapControlEvents.h"
  1. Inside MapControlEvents.h stub out all the functions that you placed in the header file.
[Motif C++]
void MapControlEvents::OnAfterDraw(VARIANT display, long viewDrawPhase){}

void MapControlEvents::OnAfterScreenDraw(long hdc){}

void MapControlEvents::OnBeforeScreenDraw(long hdc){}

void MapControlEvents::OnDoubleClick(long button, long shift, long x, long y,
  double mapX, double mapY){}

void MapControlEvents::OnExtentUpdated(VARIANT displayTransformation,
  VARIANT_BOOL sizeChanged, VARIANT newEnvelope){}

void MapControlEvents::OnFullExtentUpdated(VARIANT displayTransformation,
  VARIANT newEnvelope){}

void MapControlEvents::OnKeyDown(long keyCode, long shift){}

void MapControlEvents::OnKeyUp(long keyCode, long shift){}

void MapControlEvents::OnMapReplaced(VARIANT newMap){}

void MapControlEvents::OnMouseDown(long button, long shift, long x, long y,
  double mapX, double mapY){}

void MapControlEvents::OnMouseMove(long button, long shift, long x, long y,
  double mapX, double mapY){}

void MapControlEvents::OnMouseUp(long button, long shift, long x, long y,
  double mapX, double mapY){}

void MapControlEvents::OnOleDrop(esriControlsDropAction dropAction, VARIANT
  dataObjectHelper, long *effect, long button, long shift, long x, long y){}

void MapControlEvents::OnSelectionChanged(){}

void MapControlEvents::OnViewRefreshed(VARIANT ActiveView, long viewDrawPhase,
  VARIANT layerOrElement, VARIANT envelope){}
  1. Now implement the OnMapReplaced function, which you will need for the message, in MapControlEvents.cpp.
[Motif C++]
void MapControlEvents::OnMapReplaced(VARIANT newMap)
{
  std::cout << "New Map Loaded" << std::endl;
}

Using the events with a MapControl

Your next step is to listen for your events from an application. For the point of this walkthrough, it is assumed that you have a working application with a MapControl. If you do not have one, you can use one of the sample applications. In particular, try MapTocToolbar. To use this sample, copy its files (either the Motif or GTK version) to your folder with the event class files. For this walkthrough, the MapTocToolbar sample will be used as the base application.
For additional details on the calls used here, see the document on Event handling on ArcGIS controls.
  1. Include the header file for your class in the header file for the control application--in this case, MapTocToolbar.h.
[Motif C++]
#include "MapControlEvents.h"
  1. Create a global variable of your class type, along with an IEventListenerHelperPtr one. They will need to be deleted when the application is closed; making them globals will make that easier to do.
[Motif C++]
MapControlEvents *g_mapControlEvents;
IEventListenerHelperPtr g_ipMapControlEvent2Helper;
  1. Listen for the MapControl events from within main. This needs to be done before the top-level widget is realized.
[Motif C++]
// Listen for MapControlEvents
g_mapControlEvents = new MapControlEvents();
g_ipMapControlEvent2Helper.CreateInstance(CLSID_MapControlEvents2Listener);
g_ipMapControlEvent2Helper->Startup(static_cast < IMapControlEvents2Helper * > 
  (g_mapControlEvents));
g_ipMapControlEvent2Helper->AdviseEvents(g_ipMapControl, NULL);

// Start the application running
XtRealizeWidget(g_topLevel);
  1. Make sure to also stop listening for the events when your application closes.
[Motif C++]
// Function called when WM_DELETE_WINDOW protocol is passed
void CloseAppCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  g_ipMapControlEvent2Helper->UnadviseEvents();
  g_ipMapControlEvent2Helper->Shutdown();
  g_ipMapControlEvent2Helper = 0;
  delete g_mapControlEvents;

  // more code will follow here
}

Trying it out

Start with the appropriate makefile provided with the MapTocToolbar sample: either Makefile.SolarisMotif, Makefile.LinuxMotif, Makefile.SolarisGTK, or Makefile.LinuxGTK. You will need to update the makefile to get this command to work. First add MapControlEvents.cpp as a source:
CXXSOURCES = MapTocToolbar.cpp MapControlEvents.cpp
then add it to the dependencies list for your application and write its dependencies list:
MapTocToolbar.o: MapTocToolbar.cpp MapTocToolbar.h MapControlEvents.h
     $(CXX) $(CXXFLAGS) -c -o MapTocToolbar.o MapTocToolbar.cpp
MapControlEvents.o: MapControlEvents.cpp MapControlEvents.h
     $(CXX) $(CXXFLAGS) -c -o MapControlEvents.o MapControlEvents.cpp
Now that your makefile is set, compile and run your application. Load a new map into the MapControl, and your event will be activated and the message displayed.