ATL Window Classes

ATL contains several classes that allow you to use and implement windows. These classes, like other ATL classes, provide an efficient implementation that does not impose an overhead on your code.

This article explains how to use the ATL window classes. Topics covered include:


Using a Window

Class CWindow allows you to use a window. Once you attach a window to a CWindow object, you can then call CWindow methods to manipulate the window. CWindow also contains an HWND operator to convert a CWindow object to an HWND. Thus you can pass a CWindow object to any function that requires a handle to a window. You can easily mix CWindow method calls and Win32 function calls, without creating any temporary objects.

Because CWindow has only one data member (a window handle), it does not impose an overhead on your code. In addition, many of the CWindow methods simply wrap corresponding Win32 API functions. By using CWindow, the HWND member is automatically passed to the Win32 function.

In addition to using CWindow directly, you can also derive from it to add data or code to your class. ATL itself derives three classes from CWindow: CWindowImpl, CDialogImpl, and CContainedWindow.

Back To Top


Implementing a Window

Class CWindowImpl allows you to implement a window and handle its messages. Message handing in ATL is based on a message map. This section explains:

Message Maps

A message map associates a handler function with a particular message, command, or notification. By using ATL's message map macros, you can specify a message map for a window. The window procedures in CWindowImpl, CDialogImpl, and CContainedWindow direct a window's messages to its message map.

The message handler functions accept an additional argument of type BOOL&. This argument indicates whether a message has been processed, and it is set to TRUE by default. A handler function can then set the argument to FALSE to indicate that it has not handled a message. In this case, ATL will continue to look for a handler function further in the message map. By setting this argument to FALSE, you can first perform some action in response to a message and then allow the default processing or another handler function to finish handling the message.

ATL also allows you to chain message maps, which directs the message handling to a message map defined in another class. For example, you can implement common message handling in a separate class to provide uniform behavior for all windows chaining to that class. You can chain to a base class or to a data member of your class.

ATL also supports dynamic chaining, which allows you to chain to another object's message map at run time. To implement dynamic chaining, you must derive your class from CDynamicChain. Then declare the CHAIN_MSG_MAP_DYNAMIC macro in your message map. CHAIN_MSG_MAP_DYNAMIC requires a unique number that identifies the object and the message map to which you are chaining. You must define this unique value through a call to CDynamicChain::SetChainEntry.

You can chain to any class that declares a message map, provided the class derives from CMessageMap. CMessageMap allows an object to expose its message maps to other objects. Note that CWindowImpl already derives from CMessageMap.

Finally, ATL supports alternate message maps, declared with the ALT_MSG_MAP macro. Each alternate message map is identified by a unique number, which you pass to ALT_MSG_MAP. Using alternate message maps, you can handle the messages of multiple windows in one map. Note that by default, CWindowImpl does not use alternate message maps. To add this support, override the WindowProc method in your CWindowImpl-derived class and call ProcessWindowMessage with the message map identifier.

Implementing a Window with CWindowImpl

To implement a window, derive a class from CWindowImpl. In your derived class, declare a message map and the message handler functions. You can now use your class in three different ways:

Creating a window based on a new Windows class

CWindowImpl contains the DECLARE_WND_CLASS macro to declare Windows class information. This macro implements the GetWndClassInfo function, which uses CWndClassInfo to define the information of a new Windows class. When CWindowImpl::Create is called, this Windows class is registered and a new window is created.

Note CWindowImpl passes NULL to the DECLARE_WND_CLASS macro, which means ATL will generate a Windows class name. To specify your own name, pass a string to DECLARE_WND_CLASS in your CWindowImpl-derived class.

Following is an example of a class that implements a window based on a new Windows class:

class CMyWindow : public CWindowImpl<CMyWindow>, ... 
{ 
public: 
   // Optionally specify name of the new Windows class 
   DECLARE_WND_CLASS("MyName") 
            // If this macro is not specified in your 
            // class, ATL will generate a class name 
  ... 
   BEGIN_MSG_MAP(CMyWindow) 
      MESSAGE_HANDLER(WM_PAINT, OnPaint) 
   END_MSG_MAP() 
   LRESULT OnPaint(UINT nMsg, WPARAM wParam, 
                   LPARAM lParam, BOOL& bHandled) 
   { 
      // Do some painting code 
      return 0; 
   } 
}; 

To create a window, create an instance of CMyWindow and then call the Create method.

Note To override the default Windows class information, implement the GetWndClassInfo method in your derived class by setting the CWndClassInfo members to the appropriate values.

Superclassing an existing Windows class

The DECLARE_WND_SUPERCLASS macro allows you to create a window that superclasses an existing Windows class. Specify this macro in your CWindowImpl-derived class. Like any other ATL window, messages are handled by a message map.

When you use DECLARE_WND_SUPERCLASS, a new Windows class will be registered. This new class will be the same as the existing class you specify, but will replace the window procedure with CWindowImpl::WindowProc (or with your function that overrides this method).

Following is an example of a class that superclasses the standard Edit class:

class CMyEdit : public CWindowImpl<CMyEdit>, ... 
{ 
public: 
   // "Edit" is the name of the standard Windows class. 
   // "MyEdit" is the name of the new Windows class 
   // that will be based on the Edit class. 
   DECLARE_WND_SUPERCLASS("Edit", "MyEdit") 
   ... 
   BEGIN_MSG_MAP(CMyEdit) 
      MESSAGE_HANDLER(WM_CHAR, OnChar) 
   END_MSG_MAP() 
   LRESULT OnChar(UINT nMsg, WPARAM wParam, 
                  LPARAM lParam, BOOL& bHandled) 
   { 
      // Do some character handling code 
   } 
}; 

To create the superclassed Edit window, create an instance of CMyEdit and then call the Create method.

For more information about superclassing, see "Window Procedure Superclassing" in the Win32 SDK.

Subclassing an existing window

To subclass an existing window, derive a class from CWindowImpl and declare a message map, as in the two previous cases. Note, however, that you do not specify any Windows class information, since you will subclass an already existing window.

Instead of calling Create, call SubclassWindow and pass it the handle to the existing window you want to subclass. Once the window is subclassed, it will use CWindowImpl::WindowProc (or your function that overrides this method) to direct messages to the message map. To detach a subclassed window from your object, call UnsubclassWindow. The window's original window procedure will then be restored.

For more information about subclassing, see "Window Procedure Subclassing" in the Win32 SDK.

Back To Top


Implementing a Dialog Box

Implementing a dialog box is similar to implementing a window. You derive a class from CDialogImpl and declare a message map to handle messages. However, you must also specify a dialog template resource ID in your derived class. Your class must have a data member called IDD to hold this value.

Note When you create a dialog box using the ATL Object Wizard, the wizard automatically adds the IDD member as an enum type.

CDialogImpl allows you to implement a modal or a modeless dialog box. To create a modal dialog box, create an instance of your CDialogImpl-derived class and then call the DoModal method. To close a modal dialog box, call the EndDialog method from a message handler. To create a modeless dialog box, call the Create method instead of DoModal. To destroy a modeless dialog box, call CWindow::DestroyWindow instead of EndDialog.

Implement the dialog box's message handlers as you would the handlers in a CWindowImpl-derived class. If there is a message-specific return value, return it as an LRESULT. ATL maps the returned LRESULT values for proper handling by the Windows dialog manager. For details, see the source code for CDialogImplBase::DialogProc in atlwin.cpp.

Following is an example of a class that implements a dialog box:

class CMyDialog : public CDialogImpl<CMyDialog>, ... 
{ 
public: 
   enum { IDD = IDD_MYDIALOG }; 
   BEGIN_MSG_MAP(CMyDialog) 
      MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 
   END_MSG_MAP() 
   LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, 
                        LPARAM lParam, BOOL& bHandled) 
   { 
      // Do some initialization code 
      return 1; 
   } 
}; 

Back To Top


Using Contained Windows

ATL implements contained windows with CContainedWindow. A contained window represents a window that delegates its messages to a container object instead of handling them in its own class.

Note You do not need to derive a class from CContainedWindow in order to use contained windows.

With contained windows, you can either superclass an existing Windows class or subclass an existing window. To create a window that superclasses an existing Windows class, first specify the existing class name in the constructor for the CContainedWindow object. Then call CContainedWindow::Create. To subclass an existing window, you don't need to specify a Windows class name (pass NULL to the constructor). Simply call the CContainedWindow::SubclassWindow method with the handle to the window being subclassed.

You typically use contained windows as data members of a container class. The container does not need to be a window; however, it must derive from CMessageMap.

A contained window can use alternate message maps to handle its messages. If you have more than one contained window, you should declare several alternate message maps, each corresponding to a separate contained window.

Following is an example of a container class with two contained windows:

class CMyContainer : public CMessageMap, ... 
{ 
public: 
   CContainedWindow m_wndEdit; 
   CContainedWindow m_wndList; 
   CMyContainer() : m_wndEdit("Edit", this, 1), 
                    m_wndList("List", this, 2) 
   { 
   } 
   ... 
   BEGIN_MSG_MAP(CMyContainer) 
   ALT_MSG_MAP(1) 
      // handlers for the Edit window go here 
   ALT_MSG_MAP(2) 
      // handlers for the List window go here 
   END_MSG_MAP() 
}; 

For more information about contained windows, see the SUBEDIT sample. For more information about superclassing and subclassing, see "Window Procedure Superclassing" and "Window Procedure Subclassing" in the Win32 SDK.

Back To Top