MFC ActiveX 控件:创建 Windows 控件的子类

本文介绍了通过将常用的 Windows 控件子类化来创建 ActiveX 控件的过程。 将现有的 Windows 控件子类化是一种开发 ActiveX 控件的快速方法。 新的控件将具有已被子类化的 Windows 控件的能力,如绘制和响应鼠标单击。 MFC ActiveX 控件示例 BUTTON 是将 Windows 控件子类化的一个示例。

重要

ActiveX 是旧技术,不应用于新开发。 有关取代 ActiveX 的新式技术的详细信息,请参阅 ActiveX 控件

若要将 Windows 控件子类化,请完成以下任务:

重写 IsSubclassedControl 和 PreCreateWindow

若要重写 PreCreateWindowIsSubclassedControl,请向控件类声明的 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;
}

请注意,在此示例中,将在 PreCreateWindow 中指定 Windows 按钮控件。 但是,任何标准 Windows 控件都可以子类化。 有关标准 Windows 控件的详细信息,请参阅控件

在子类化 Windows 控件时,可能需要指定在创建控件的窗口时要使用的特定窗口样式 (WS_) 或扩展窗口样式 (WS_EX_) 标志。 你可以通过修改 cs.stylecs.dwExStyle 结构字段,在 PreCreateWindow 成员函数中设置这些参数的值。 应使用 OR 操作对这些字段进行修改,以保留由 COleControl 类设置的默认标志。 例如,如果控件将对 BUTTON 控件进行子类化,而你希望控件显示为复选框,则应在 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。 它演示了一个检测 BN_CLICKED 通知代码并通过激发(发送)Click 事件进行响应的 OnOcmCommand 处理程序。

另请参阅

MFC ActiveX 控件