本文介绍创建 ActiveX 控件的常见 Windows 控件的子类化过程。 对现有 Windows 控件进行子类化是开发 ActiveX 控件的快速方法。 新控件将具备与子类化的 Windows 控件相同的功能,例如绘制和响应鼠标点击。 MFC ActiveX 控件示例 BUTTON 是一个子类化 Windows 控件的示例。
重要
ActiveX 是一项不推荐用于新开发的旧技术。 有关取代 ActiveX 的新式技术的详细信息,请参阅 ActiveX 控件。
若要将 Windows 控件子类化,请完成以下任务:
-
注释
如果你选择控件以使用“控件设置”页上的“选择父窗口类”下拉列表进行子类化,则 ActiveX 控件向导会为你完成大部分工作。
重写 IsSubclassedControl 和 PreCreateWindow
若要重写 PreCreateWindow 和 IsSubclassedControl,请向控件类声明的 protected 部分添加以下代码行:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
BOOL IsSubclassedControl();
在控件实现文件 (.cpp) 中,添加以下代码行以实现两个重写的函数:
// 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;
}
请注意,在此示例中,Windows 按钮控件是在 PreCreateWindow 中指定的。 但是,任何标准 Windows 控件都可以进行子类化。 有关标准 Windows 控件的详细信息,请参阅 “控件”。
对 Windows 控件进行子类化时,可能需要指定用于创建控件窗口窗口的特定窗口样式(WS_)或扩展窗口样式(WS_EX_)标志。 可以通过修改PreCreateWindow成员函数和cs.style结构字段来设置这些参数cs.dwExStyle的值。 应使用 OR 作对这些字段进行修改,以保留由类 COleControl设置的默认标志。 例如,如果控件是 BUTTON 控件的子类,并且您希望该控件显示为复选框,请在 return 语句之前将以下代码行插入到 CSampleCtrl::PreCreateWindow 的实现中:
cs.style |= BS_CHECKBOX;
此作将添加BS_CHECKBOX样式标志,同时保留类 COleControl 的默认样式标志(WS_CHILD)。
修改 OnDraw 成员函数
如果希望子类化控件保持与相应 Windows 控件相同的外观,控件 OnDraw 的成员函数应仅包含对 DoSuperclassPaint 成员函数的调用,如以下示例所示:
void CMyAxSubCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
if (!pdc)
return;
DoSuperclassPaint(pdc, rcBounds);
}
由 DoSuperclassPaint 实现的 COleControl 成员函数使用 Windows 控件的窗口过程,在指定的设备上下文中的边框内绘制控件。 这样,即使控件不处于活动状态,控件也可见。
注释
DoSuperclassPaint 成员函数仅适用于允许设备上下文作为 WM_PAINT 消息的 wParam 传递的控件类型。 这包括一些标准 Windows 控件,例如 SCROLLBAR 和 BUTTON,以及所有通用控件。 对于不支持此行为的控件,必须提供自己的代码才能正确显示非活动控件。
处理反射窗口消息
Windows 控件通常会将某些窗口消息发送到其父窗口。 这些消息中的一些(例如 WM_COMMAND)用于通知用户的某个操作。 其他方法(如WM_CTLCOLOR)用于从父窗口获取信息。 ActiveX 控件通常通过其他方式与父窗口通信。 通知通过触发事件(发送事件通知)进行通信,并通过访问容器的环境属性获取有关控制容器的信息。 由于存在这些通信技术,因此 ActiveX 控件容器不应处理控件发送的任何窗口消息。
若要防止容器接收子类化的 Windows 控件发送的窗口消息,COleControl 请创建一个额外的窗口来作为控件的父窗口。 此额外的窗口(称为“反射器”)仅为对 Windows 控件进行子类化的 ActiveX 控件创建,其大小和位置与控件窗口相同。 反射器窗口截获某些窗口消息,并将其发送回控件。 然后,控件在其窗口过程中可以通过执行适用于 ActiveX 控件的作(例如触发事件)来处理这些反映的消息。 有关已截获的窗口消息的列表及其对应的反射消息,请参阅 反射窗口消息 ID 。
ActiveX 控件容器可以设计为自行执行消息反射,从而不必使用 COleControl 来创建反射器窗口,减少了子类化 Windows 控件的运行时开销。 COleControl 通过检查值为 TRUE 的 MessageReflect 环境属性,检测容器是否支持此功能。
若要处理反射的窗口消息,请将条目添加到控件消息映射并实现处理程序函数。 由于反映的消息不是 Windows 定义的标准消息集的一部分,因此类视图不支持添加此类消息处理程序。 手动添加一个处理程序其实并不难。
若要手动为反射窗口消息添加消息处理程序,请执行以下作:
在控件类的 .h 文件中,声明一个处理函数。 该函数应具有一个返回类型的 LRESULT 和两个参数,分别具有 WPARAM 和 LPARAM 类型。 例如:
class CMyAxSubCtrl : public COleControl {protected: LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam); };在控件类的.CPP文件中,将ON_MESSAGE条目添加到消息映射。 此条目的参数应为消息标识符和处理程序函数的名称。 例如:
BEGIN_MESSAGE_MAP(CMyAxSubCtrl, COleControl) ON_MESSAGE(OCM_COMMAND, &CMyAxSubCtrl::OnOcmCommand) END_MESSAGE_MAP()同样在 .CPP 文件中,实现
OnOcmCommand成员函数以处理反射消息。 wParam 和 lParam 参数与原始窗口消息的参数相同。
有关如何处理反映的消息的示例,请参阅 MFC ActiveX 控件示例 BUTTON。 它演示了一个 OnOcmCommand 处理程序,该处理程序通过触发(发送)事件 Click 来检测BN_CLICKED通知代码并做出响应。