Programming ArcGIS in Visual C++  

ATL and the ActiveX Controls

This topic is relevant for the following:
Product(s): ArcGIS Desktop: All; ArcGIS Engine; ArcGIS Server
Version(s): 9.2, 9.3
Language(s): VC++
Experience level(s): Intermediate to advanced


This section covers how to use ATL to add controls to a dialog box. For a general discussion of ATL, see the section ATL in Brief. In addition, the steps to produce an ATL component are described in detail in the Walkthrough.

In this topic:

  1. Accessing a Control on a Dialog Box Through a COM Interface
  2. Listening to Events from a Control
  3. Creating a Control at Run Time
  4. Setting the Buddy Control Property
  5. Known Limitations of Visual Studio C++ Resource Editor

Although ATL is focused on providing COM support, it also supplies some useful Windows programming wrapper classes. One of the most useful is CWindow,  a wrapper around a window handle (HWND). The method names on CWindow correspond to the Win32 API functions. For example:


HWND buttonHWnd = GetDlgItem( IDC_BUTTON1 );       // Get window handle of button
CWindow myButtonWindow( buttonHWnd );              // Attach window handle to CWindow class
myButtonWindow.SetWindowText(_T("Button Title"));  // Win32 function to change button caption

CWindow is a generic wrapper for all window handles, so for specific Windows messages to Windows common controls, such as buttons, tree views, or edit boxes, one approach is to send window messages directly to the window, for example:


// Set button to be checked (pushed in or checkmarked, depending on button style)
myButtonWindow.SendMessage(BM_SETCHECK, BST_CHECKED); 

However, there are some wrapper classes for these standard window common controls in a header file atlcontrols.h. This is available as part of an ATL sample ATLCON supplied in MSDN. See the article 'HOWTO: Using Class Wrappers to Access Windows Common Controls in ATL', available for download from Microsoft. This header file is an early version of Windows Template Libraries (WTL).

The Visual Studio Resource Editor can be used to design and position Windows common controls and ActiveX controls on a dialog box. To create and manipulate the dialog box, a C++ class is typically created that inherits from CAxDialogImpl. This class provides the plumbing to create and manage the ActiveX control on a window. The ATL wizard can be used to supply the majority of the boilerplate code. The steps to create a dialog box and add an ActiveX control in an ATL project in VC8 are discussed below.

  1. Right click on the project in the soultions tab and choose Add -> Class...

  2. Choose ATL and ATL Dialog and then click the Add button.

  3. Type in the name of your dialog and click Finish.

  4. A dialog box resource and a class inheriting from CAxDialogImpl will be added to your project.

  5. Right-click the dialog box in Resource view and click Insert ActiveX Control. This will display a list of available ActiveX controls.

  6. Double-click a control in the list to add the control to the dialog box.

  7. Right-click the control and click Properties to set the control's design-time properties.

    Beware! Make sure dialog boxes that host ActiveX controls inherit from CAxDialogImpl and not CDialogImpl. If this mistake is made, the DoModal method of the dialog box exits with no obvious cause.

    Beware! Make sure applications that use Windows common controls, such as treeview, correctly call InitCommonControlsEx to load the window class. Otherwise, the class will not function correctly. 

Accessing a Control on a Dialog Box Through a COM Interface

To retrieve a handle to the control that is hosted on a form, use the GetDlgControl ATL method that is inherited from CAxDialogImpl to take a resource ID and return the underlying control pointer:


ITOCControlPtr ipTOCControl;
GetDlgControl(IDC_TOCCONTROL1, IID_ITOCControl, (void**) &ipTOCControl);
ipTOCControl->AboutBox();

Beware! Make sure applications using COM objects call CoInitialize. This initializes COM in the application. Without this call, any CoCreate calls will fail.

In order to display your dialog you need to create an instance of it. You might want to do this in the main function implemented by your module class. The main function gets automatically injected by the [module] attribute into your module class and needs to be overwritten. See section "ATL and Attributes" in ATL Discussion for a code sample.

Listening to Events from a Control

For a detailed discussion on handling events in ATL , see the section Handling COM Events in ATL. The simplest way to add events in VC8 is to use the class wizard. Right-click the control and choose Add Event Handler and select the event (for example, OnMouseDown). Next choose the event sink from the class list and click the Add and Edit button. Finally, ensure the dialog box begins listening to events by adding AtlAdviseSinkMap(this,TRUE) to OnInitDialog. To finish listening to events, add a message handler for OnDestroy and add a call to AtlAdviseSinkMap(this, FALSE).

Creating a Control at Runtime

The CAxWindow class provides a mechanism to create and host ActiveX controls in a similar manner to any other window class. This may be desirable if the parent window of the control is also created at runtime.


AtlAxWinInit();
CAxWindow wnd;
//m_hWnd is the parent window handle
//rect is the size of ActiveX control in client coordinates
//IDC_MYCTL is a unique ID to identify the controls window
RECT rect = {10,10,400,300};
wnd.Create(m_hWnd, rect, _T("esriReaderControl.ReaderControl"), WS_CHILD|WS_VISIBLE, 0, IDC_MYCTL);  

Setting the Buddy Control Property

The ToolbarControl and TOCControl need to be associated with a "buddy" control on the dialog box. This is typically performed in the OnInitDialog windows message handler of a dialog box.


LRESULT CEngineControlsDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
  // Get the Control's interfaces into class member variables 
  GetDlgControl(IDC_TOOLBARCONTROL, IID_IToolbarControl, (void **) &m_ipToolbarControl);
  GetDlgControl(IDC_TOCCONTROL, IID_ITOCControl, (void **) &m_ipTOCControl);
  GetDlgControl(IDC_PAGELAYOUTCONTROL, IID_IPageLayoutControl, (void **) &m_ipPageLayoutControl);

  // Connect to the controls
  AtlAdviseSinkMap(this, TRUE);

  // Set buddy controls
  m_ipTOCControl->SetBuddyControl(m_ipPageLayoutControl);
  m_ipToolbarControl->SetBuddyControl(m_ipPageLayoutControl);

  return TRUE;
}

Known Limitations of Visual Studio Resource Editor and ArcGIS ActiveX controls

Disabled buddy property on property page
In Visual Studio you cannot set the buddy property of the TOCControl and the ToolbarControl through the General property page. Visual C++ does not support controls finding other controls at design time. However, this step can be performed in code in the OnInitDialog method.

ToolbarControl not resized to the height of one button
In other environments (Visual Basic 6, .NET) the ToolbarControl will automatically resize to be one button high. However, in Visual C++ it can be any size. In MFC and ATL, the ActiveX host classes do not allow controls to determine their own size.