MFC ActiveX Controls: Subclassing a Windows Control
The latest version of this topic can be found at MFC ActiveX Controls: Subclassing a Windows Control.
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 is an example of subclassing a Windows control.
To subclass a Windows control, complete the following tasks:
Override the IsSubclassedControl and PreCreateWindow member functions of COleControl
Modify the OnDraw member function
Handle any ActiveX control messages (OCM) reflected to the control
Note
Much of this work is done for you by the ActiveX Control Wizard if you select control to be subclassed using the Select Parent Window Class drop-down list on the Control Settings page.
See Knowledge Base article Q243454 for more information on subclassing a control.
Overriding IsSubclassedControl and PreCreateWindow
To override PreCreateWindow
and IsSubclassedControl
, add the following lines of code to the protected
section of the control class declaration:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
BOOL IsSubclassedControl();
In the control implementation file (.CPP), add the following lines of code to implement the two overridden functions:
// CMyAxSubCtrl::PreCreateWindow - Modify parameters for CreateWindowEx
BOOL CMyAxSubCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = _T("BUTTON");
return COleControl::PreCreateWindow(cs);
}
// CMyAxSubCtrl::IsSubclassedControl - This is a subclassed control
BOOL CMyAxSubCtrl::IsSubclassedControl()
{
return TRUE;
}
Notice that, in this example, the Windows button control is specified in PreCreateWindow
. However, any standard Windows controls can be subclassed. For more information on standard Windows controls, see Controls.
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.
Modifying the OnDraw Member Function
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 CMyAxSubCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
if (!pdc)
return;
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 the common controls. For controls that do not support this behavior, you will have to provide your own code to properly display an inactive control.
Handling Reflected Window Messages
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). See Reflected Window Message IDs for a list of intercepted windows messages and their corresponding reflected messages.
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, Class View 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:
In the control class .H file, declare a handler function. The function should have a return type of LRESULT and two parameters, with types WPARAM and LPARAM, respectively. For example:
class CMyAxSubCtrl : public COleControl {
protected: LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam); };
In the control class .CPP file, add an
ON_MESSAGE
entry to the message map. The parameters of this entry should be the message identifier and the name of the handler function. For example:BEGIN_MESSAGE_MAP(CMyAxSubCtrl, COleControl) ON_MESSAGE(OCM_COMMAND, &CMyAxSubCtrl::OnOcmCommand) END_MESSAGE_MAP()
Also in the .CPP file, implement the OnOcmCommand member function to process the reflected message. The wParam and lParam parameters are the same as those of the original window message.
For an example of how reflected messages are processed, refer to the MFC ActiveX controls sample BUTTON. It demonstrates an OnOcmCommand handler that detects the BN_CLICKED notification code and responds by firing (sending) a Click event.