Home | Overview | How Do I | FAQ | Tutorial | Sample
This article describes the process for subclassing a common Windows control to create an ActiveX control. Subclassing an existing Windows control is a quick way to develop an ActiveX control. The new control will have the abilities of the subclassed Windows control, such as painting and responding to mouse clicks. The MFC ActiveX controls sample BUTTON, listed under CONTROLS, is an example of subclassing a Windows control.
To subclass a Windows control, complete the following tasks:
Note Much of this work is done for you by ControlWizard if you select the Subclass Windows Control option in the Control Options dialog box.
To override PreCreateWindow and IsSubclassedControl, add the following lines of code to the protected section of the control class declaration:
BOOL PreCreateWindow( CREATESTRUCT& cs );
BOOL IsSubclassedControl( );
In the control implementation file (.CPP), add the following lines of code to implement the two overridden functions:
BOOL CSampleCtrl::PreCreateWindow( CREATESTRUCT& cs )
{
cs.lpszClass = _T("BUTTON");
return COleControl::PreCreateWindow(cs);
}
BOOL CSampleCtrl::IsSubclassedControl( )
{
return TRUE;
}
Notice that, in this example, the Windows button control is specified in PreCreateWindow. However, any of the standard Windows controls can be subclassed. For more information on standard Windows controls, see Controls: Overview.
When subclassing a Windows control, you may want to specify particular window style (WS_) or extended window style (WS_EX_) flags to be used in creating the control's window. You can set values for these parameters in the PreCreateWindow member function by modifying the cs.style and the cs.dwExStyle structure fields. Modifications to these fields should be made using an OR operation, to preserve the default flags that are set by class COleControl. For example, if the control is subclassing the BUTTON control and you want the control to appear as a check box, insert the following line of code into the implementation of CSampleCtrl::PreCreateWindow
, before the return statement:
cs.style |= BS_CHECKBOX;
This operation adds the BS_CHECKBOX style flag, while leaving the default style flag (WS_CHILD) of class COleControl intact.
If you want your subclassed control to keep the same appearance as the corresponding Windows control, the OnDraw
member function for the control should contain only a call to the DoSuperclassPaint member function, as in the following example:
void CSampleCtrl::OnDraw( CDC* pdc, const CRect& rcBounds,
const CRect& rcInvalid )
{
DoSuperclassPaint( pdc, rcBounds );
}
The DoSuperclassPaint member function, implemented by COleControl, uses the window procedure of the Windows control to draw the control in the specified device context, within the bounding rectangle. This makes the control visible even when it is not active.
Note The DoSuperclassPaint member function will work only with those control types that allow a device context to be passed as the wParam of a WM_PAINT message. This includes some of the standard Windows controls, such as SCROLLBAR and BUTTON, and all of the Windows 95 common controls. For controls that do not support this behavior, you will have to provide your own code to properly display an inactive control.
Windows controls typically send certain window messages to their parent window. Some of these messages, such as WM_COMMAND, provide notification of an action by the user. Others, such as WM_CTLCOLOR, are used to obtain information from the parent window. An ActiveX control usually communicates with the parent window by other means. Notifications are communicated by firing events (sending event notifications), and information about the control container is obtained by accessing the container’s ambient properties. Because these communication techniques exist, ActiveX control containers are not expected to process any window messages sent by the control.
To prevent the container from receiving the window messages sent by a subclassed Windows control, COleControl creates an extra window to serve as the control's parent. This extra window, called a “reflector”, is created only for an ActiveX control that subclasses a Windows control and has the same size and position as the control window. The reflector window intercepts certain window messages and sends them back to the control. The control, in its window procedure, can then process these reflected messages by taking actions appropriate for an ActiveX control (for example, firing an event).
The following table shows the messages that are intercepted and the corresponding messages that the reflector window sends.
Reflected Windows Messages
Message sent by control | Message reflected to control |
WM_COMMAND | OCM_COMMAND |
WM_CTLCOLOR | OCM_CTLCOLOR |
WM_DRAWITEM | OCM_DRAWITEM |
WM_MEASUREITEM | OCM_MEASUREITEM |
WM_DELETEITEM | OCM_DELETEITEM |
WM_VKEYTOITEM | OCM_VKEYTOITEM |
WM_CHARTOITEM | OCM_CHARTOITEM |
WM_COMPAREITEM | OCM_COMPAREITEM |
WM_HSCROLL | OCM_HSCROLL |
WM_VSCROLL | OCM_VSCROLL |
WM_NOTIFY | OCM_NOTIFY |
WM_PARENTNOTIFY | OCM_PARENTNOTIFY |
Note If the control runs on a Win32 system, there are several types of WM_CTLCOLOR messages it may receive instead of WM_CTLCOLOR. For more information, see WM_CTLCOLORBTN, WM_CTLCOLORDLG, WM_CTLCOLOREDIT, WM_CTLCOLORLISTBOX, WM_CTLCOLORMSGBOX, WM_CTLCOLORSCROLLBAR, WM_CTLCOLORSTATIC.
An ActiveX control container may be designed to perform message reflection itself, eliminating the need for COleControl to create the reflector window and reducing the run-time overhead for a subclassed Windows control. COleControl detects whether the container supports this capability by checking for a MessageReflect ambient property with a value of TRUE.
To handle a reflected window message, add an entry to the control message map and implement a handler function. Because reflected messages are not part of the standard set of messages defined by Windows, ClassWizard does not support adding such message handlers. However, it is not difficult to add a handler manually.
To add a message handler for a reflected window message manually do the following:
class CSampleCtrl : public COleControl
{
protected:
LRESULT OnOcmCommand( WPARAM wParam, LPARAM lParam );
...
}
BEGIN_MESSAGE_MAP(CSampleCtrl, COleControl)
//{{AFX_MSG_MAP(CSampleCtrl)
...
ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
...
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
For an example of how reflected messages are processed, refer to the MFC ActiveX controls sample BUTTON, listed under CONTROLS. It demonstrates an OnOcmCommand handler that detects the BN_CLICKED notification code and responds by firing (sending) a Click event.