How to create a custom command using AoBaseCommand


With ArcGIS Engine, you can write custom commands to add to your applications. Your custom command will be a button or menu that performs a simple action when clicked or selected. It allows you to easily add custom functionality to your ArcGIS control applications.
If you are looking to interact with the ArcGIS controls' display--for example, as the zoom in tool does—you will need to write a custom tool. For further details, see Creating a custom tool using AoToolBase.
Since commands are a GUI tool to interact with ArcGIS controls, C++ custom commands will only work on Solaris and Linux. If you want to write a command or tool to plug into ArcMap or ArcCatalog, you will need to use a different language such as Visual C++.
To create a new command, you will use the esriSystemUI ICommand interface. Through the ICommand interface, you will be able to set the properties and behavior for your command. Some of the properties that you can set through the ICommand interface are the command's name, bitmap, caption, category, statusbar message, tooltip, enabled state, and checked state. It also defines the action taken when your command is clicked.
To create custom commands with the C++ API, there is a helper class for ICommand that you will inherit from: CAoCommandBase. This is defined in arcgis/developerkit10.0/include/Ao/AoCommandBase.h and includes AoToolbarAddCommand, which you will use to place your custom command on a ToolbarControl.

Command-creation example: Custom Full-Extent Command

This command will zoom in by a factor of two to the current map and can be used in any application using a ToolbarControl with a MapControl or PageLayoutControl buddy.
Creating the ZoomIn class
  1. Create a new file in your text editor: ZoomIn.h.
  2. In ZoomIn.h, create a new class, ZoomIn. Include a public constructor and destructor, as well as a private IHookHelperPtr data member, which will contain the hook object passed to the command when the command is created. This member variable will provide access to the ActiveView, FocusMap, and PageLayout of whichever control the command is associated with: a MapControl, PageLayoutControl, or ToolbarControl. You will need to include ArcSDK.h for the Engine.
[Motif C++]
          #ifndef __ZOOM_IN_H_
  #define __ZOOM_IN_H_

  // ArcObjects Headers// Engine#include <ArcSDK.h>

  class ZoomIn
  {
    public:
      ZoomIn();
      ~ZoomIn();

    private:
      IHookHelperPtr m_ipHookHelper;
  };

#endif// #define __ZOOM_IN_H_
  1. Update the ZoomIn class so that it inherits from CAoCommandBase. Add the functions from CAoCommandBase, which you need to implement. Also add a private member variable of type OLE_HANDLE, which you will use to add a bitmap to your custom command. You will need to include Ao/AoCommandBase.h for CAoCommandBase and AoToolbarAddCommand. You will also need to include Ao/AoControls.h since it is used in Ao/AoCommandBase.
[Motif C++]
          #ifndef __ZOOM_IN_H_
  #define __ZOOM_IN_H_

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

  // Custom Command#include <Ao/AoCommandBase.h>

  class ZoomIn: public CAoCommandBase
  {
    public:
      ZoomIn();
      ~ZoomIn();

      // ICommand
      HRESULT get_Enabled(VARIANT_BOOL *Enabled);
      HRESULT get_Checked(VARIANT_BOOL *Checked);
      HRESULT get_Name(BSTR *Name);
      HRESULT get_Caption(BSTR *Caption);
      HRESULT get_Tooltip(BSTR *Tooltip);
      HRESULT get_Message(BSTR *Message);
      HRESULT get_Bitmap(OLE_HANDLE *bitmapFile);
      HRESULT get_Category(BSTR *categoryName);
      HRESULT OnCreate(IDispatch *hook);
      HRESULT OnClick();

    private:
      IHookHelperPtr m_ipHookHelper;
      OLE_HANDLE m_hBitmap;
  };

#endif// #define __ZOOM_IN_H_

Adding code to the members of AoCommandBase

  1. Create a new file in your text editor: ZoomIn.cpp.
  2. Include ZoomIn.h.
[Motif C++]
          #include
          "ZoomIn.h"
        
  1. Implement the constructor after the include. Here you will load in the bitmap for the button. You do not need to initialize m_ipHookHelper since it is a smart pointer. Smart pointers are initialized by default to 0. If this were a standard interface pointer, it would have to be initialized in the constructor's initialization list.
[Motif C++]
ZoomIn::ZoomIn()
{
  // Load the bitmap
  IRasterPicturePtr ipRastPict(CLSID_BasicRasterPicture);
  IPicturePtr ipPict;
  HRESULT hr = ipRastPict->LoadPicture(CComBSTR(L "zoominfxd.bmp"), &ipPict);
  if (SUCCEEDED(hr))
  {
    OLE_HANDLE hBitmap;
    hr = ipPict->get_Handle(&hBitmap);
    if (SUCCEEDED(hr))
      m_hBitmap = hBitmap;
  }
}
  1. Next implement the class destructor, where you will release the member variables.
[Motif C++]
ZoomIn::~ZoomIn()
{
  m_ipHookHelper = 0;
  m_hBitmap = 0;
}
  1. In the following few steps you will implement the AoCommandBase functions.
    1. Although every function must be stubbed out, not all of them need to be implemented. For example, in get_Checked the argument is checked, but then the function simply returns E_NOTIMPL if the parameter is valid.
[Motif C++]
HRESULT ZoomIn::get_Checked(VARIANT_BOOL *Checked)
{
  if (Checked == NULL)
    return E_POINTER;
  return E_NOTIMPL;
}
    1. Most of the following functions involve simply setting a parameter. Notice the use of VARIANT_TRUE and API calls to create the strings.
[Motif C++]
HRESULT ZoomIn::get_Enabled(VARIANT_BOOL *Enabled)
{
  if (!Enabled)
    return E_POINTER;
  *Enabled = VARIANT_TRUE; // Always enable the commandreturn S_OK;
}

HRESULT ZoomIn::get_Name(BSTR *Name)
{
  if (!Name)
    return E_POINTER;
  *Name = ::AoAllocBSTR(L "Zoom In x 0.5 C++");
  return S_OK;
}

HRESULT ZoomIn::get_Caption(BSTR *Caption)
{
  if (!Caption)
    return E_POINTER;
  *Caption = ::AoAllocBSTR(L "Zoom In x 0.5 C++");
  return S_OK;
}

HRESULT ZoomIn::get_Tooltip(BSTR *Tooltip)
{
  if (!Tooltip)
    return E_POINTER;
  *Tooltip = ::AoAllocBSTR(L "Zoom In x 0.5");
  return S_OK;
}

HRESULT ZoomIn::get_Message(BSTR *Message)
{
  if (!Message)
    return E_POINTER;
  *Message = ::AoAllocBSTR(L "Zoom in on the map by a factor of 2");
  return S_OK;
}

HRESULT ZoomIn::get_Category(BSTR *categoryName)
{
  if (!categoryName)
    return E_POINTER;
  *categoryName = ::AoAllocBSTR(L "Developer Samples");
  return S_OK;
}
    1. Since you already have the OLE_HANDLE for the bitmap, setting the command's bitmap is a simple assignment.
[Motif C++]
HRESULT ZoomIn::get_Bitmap(OLE_HANDLE *bitmap)
{
  if (!bitmap)
    return E_POINTER;
  if (m_hBitmap != 0)
  {
    *bitmap = m_hBitmap;
    return S_OK;
  }
  return E_FAIL;
}
    1. The OnCreate function is passed a pointer to the IDispatch interface of the object. Using the QueryInterface support of the smart pointer, it is a simple matter to set the member variable to be the hook. The smart pointer handles the QI.
[Motif C++]
HRESULT ZoomIn::OnCreate(IDispatch *hook)
{
  m_ipHookHelper.CreateInstance(CLSID_HookHelper);
  if (!hook)
    return E_POINTER;
  m_ipHookHelper->putref_Hook(hook);
  return S_OK;
}
    1. The OnClick method is implemented to zoom the display by a factor of two. There is no error checking to simplify the code.
[Motif C++]
HRESULT ZoomIn::OnClick()
{
  // Get IActiveView interface
  IActiveViewPtr ipActiveView;
  m_ipHookHelper->get_ActiveView(&ipActiveView);
  // Get IEnvelope interface & the current extent
  IEnvelopePtr ipEnv;
  ipActiveView->get_Extent(&ipEnv);
  // Expand the envelop and refresh the view
  ipEnv->Expand(0.5, 0.5, VARIANT_TRUE);
  ipActiveView->put_Extent(ipEnv);
  ipActiveView->Refresh();
  return S_OK;
}

Using the command with the ToolbarControl

Your next step is to programmatically place your custom ZoomIn command on a toolbar. For the point of this walkthrough, it is assumed that you have a working application with a ToolbarControl and either a MapControl or a PageLayoutControl. If you do not have one, you can use one of the sample applications. In particular, try MapTocToolbar or PageLayout. To use this sample, copy its files (either the Motif or GTK version) to your folder with the custom ZoomIn command. For this walkthrough, the MapTocToolbar sample will be used as the base application.

Creating an instance of your command
  1. Include the header file for your command in the header file for the control application, here MapTocToolbar.h.
[Motif C++]
          #include
          "ZoomIn.h"
        
  1. Create a global variable of your class type. Since it will need to be deleted when the application is closed, having a global will make that easier to do.
[Motif C++]
ZoomIn *g_zoomIn;
  1. Create a new instance of your class. You will do this right before adding it to the toolbar. For the MapTocToolbar application, this would be done in AddToolbarItems, and that is where the surrounding grayed out code is from.
[Motif C++]
          void AddToolbarItems()
{
  ... varTool = L "esriControlCommands.ControlsSelectTool";
  g_ipToolbarControl->AddItem(varTool, 0,  - 1, VARIANT_FALSE, 0,
    esriCommandStyleIconOnly, &itemIndex);
  g_zoomIn = new ZoomIn();
}
Placing your command on the ToolbarControl
With the help of AoToolbarAddCommand this becomes a simple step of providing what ToolbarControl to add the command to, the instance of the command, and the display style for the command. Since you have provided a button, in this example it will be placed on the ToolbarControl with only an icon. It is added to g_ipToolbarControl because that is the ToolbarControl in MapTocToolbar.
[Motif C++]
          void AddToolbarItems()
{
  ... g_zoomIn = new ZoomIn();
  AoToolbarAddCommand(g_ipToolbarControl, g_zoomIn, esriCommandStyleIconOnly);
}

Trying it out

Start with the appropriate makefile provided with the MapTocToolbar sample: Makefile.SolarisMotif, Makefile.LinuxMotif, Makefile.SolarisGTK, or Makefile.LinuxGTK. You will need to update the makefile to get this command to work. First add ZoomIn.cpp as a source:
CXXSOURCES = MapTocToolbar.cpp ZoomIn.cpp
then add it to the dependencies list for your application and write its dependencies list:
MapTocToolbar.o: MapTocToolbar.cpp MapTocToolbar.h ZoomIn.h
     $(CXX) $(CXXFLAGS) -c -o MapTocToolbar.o MapTocToolbar.cpp
ZoomIn.o: ZoomIn.cpp ZoomIn.h
     $(CXX) $(CXXFLAGS) -c -o ZoomIn.o ZoomIn.cpp
Now that your makefile is set, compile and run your application. Your custom command will be on the toolbar, and you can use it to zoom in by a factor of two on the current map.