65.9K
CodeProject 正在变化。 阅读更多。
Home

从 MFC 属主绘制的 Button 创建 ActiveX Button

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (35投票s)

2002年4月25日

5分钟阅读

viewsIcon

302144

downloadIcon

6925

将 MFC 按钮转换为 ActiveX 控件的基本步骤

引言

有许多使用 C++ 和 MFC 类编写的所有者绘图按钮。也有许多 ActiveX 控件,大多是用 Visual Basic 编写的。虽然用 C++,您可以轻松使用 MFC 和 OCX 控件,但用 Visual Basic,您需要将 MFC 对象转换为 ActiveX 控件。不幸的是,类向导并不涵盖发送到 ActiveX 控件的所有消息和事件,并且一些消息是不同的,因此大部分的魔法都必须手动编写。

因为有时,一段代码比文章本身更清晰,所以我包含了将 CxShadeButton 移植到 ActiveX 控件的代码,但这只是一个例子。在文章中,我将讨论一个通用的 AxButtonCtrl

MFC ActiveX 控件向导 & 类向导

通过几次点击,ActiveX 控件向导会为我们编写大约 600 行带注释的代码,我们可能永远不会阅读。只需记住在向导询问“此控件是否会子类化某个窗口类(如果有)?”的组合框中选择“BUTTON”,框架就已准备就绪。

使用类向导,您可以添加成员函数来处理基本消息

  • WM_CREATE
  • WM_ERASEBKGND
  • WM_KEYDOWN
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_MOUSEMOVE
  • WM_SIZE
  • PreSubclassWindow

其中一些消息是可选的,但我在考虑自定义控件,因此处理的消息可能比普通按钮多。
WM_DRAWITEM 消息不在列表中,这不是错误,我将在 稍后解释。

PreSubclassWindow

void AxuttonCtrl::PreSubclassWindow() 
{
	... //custom style initialization code	
	COleControl::PreSubclassWindow();
	ModifyStyle(0, BS_OWNERDRAW|BS_NOTIFY);
}

在此方法中,您可以复制用于 MFC 控件的相同代码;不同之处在于最后两行:按钮现在派生自 COleControl(而不是 CButton)。您必须设置 BS_OWNERDRAW 样式以使用自定义图形绘制按钮,以及 BS_NOTIFY 样式以获取某些特殊通知消息,例如 BN_DISABLEBN_KILLFOCUS 等。

WM_CREATE & WM_SIZE

这些消息有时不被 MFC 控件使用,但在 Visual Basic 中,所见即所得的理念要求开发人员在构建 GUI 时可以看到外观。
控件接收 WM_CREATE 消息并通过 COleControl::OnCreate(lpCreateStruct) 创建对象,调用此函数后按钮就存在了,您可以使用所有窗口函数(MFC 中的 CWnd 成员)来初始化图形对象。
WM_SIZE 消息在 WM_CREATE 之后发送,并在按钮大小改变时发送;在这里,您必须构建(或重建)图形对象的位置和/或尺寸。

键盘 & 鼠标消息

这些消息对于工具提示和悬停功能很有用,请参阅 稍后

绘制按钮 - 反射窗口消息

类向导为绘图函数提供了一个 AxButtonCtrl::OnDraw 成员。

void AxButtonCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
	DoSuperclassPaint(pdc, rcBounds);
}

您可以将其保留原样。我们的按钮等待 WM_DRAWITEM 消息,但 ActiveX 控件的机制略有不同:COleControl 创建一个额外的窗口,称为“反射器”,位于控件窗口的相同位置。反射器会拦截某些窗口消息,并以不同的名称将它们发送给控件。反射的消息是

控件发送的消息 反射给控件的消息
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

对于这些消息,您必须手动添加消息处理程序:在控件类的 .H 文件中,声明一个类似这样的处理程序函数

class AxButtonCtrl : public COleControl
{
...
 protected:
LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam);
LRESULT OnOcmDrawItem(WPARAM wParam, LPARAM lParam);
...
}

在控件类的 .CPP 文件中,将 ON_MESSAGE 条目添加到消息映射中

BEGIN_MESSAGE_MAP(AxButtonCtrl, COleControl)
//{{AFX_MSG_MAP(AxButtonCtrl)
...
ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
ON_MESSAGE(OCM_DRAWITEM, OnOcmDrawItem)
...
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

并实现成员函数来处理反射的消息。wParamlParam 参数与原始窗口消息的参数相同。

LRESULT AxButtonCtrl::OnOcmDrawItem(WPARAM wParam, LPARAM lParam)
{
	UINT nIDCtl = (UINT) wParam;
	LPDRAWITEMSTRUCT lpDrawItemStruct = (LPDRAWITEMSTRUCT) lParam;
	... //drawing code as used in WM_DRAWITEM
	return 0;
}

事件

类向导为标准事件提供了一些内置处理程序,但结果功能可能有限。如果您添加带有内置处理程序实现的“Click”事件,当有人用鼠标单击按钮时,COleButton 会自动调用 FireClick 方法,因此您无需处理 OCM_COMMAND 消息。但是,如果按钮具有焦点且有人按下空格键,则不会调用 FireClick 方法,尽管按钮通过 OCM_COMMAND 消息接收 BN_CLICKED 通知。

最后,使用自定义事件处理程序实现,这样您就可以精确控制按钮的行为,而无需隐藏的调用或消息。

LRESULT AxButtonCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
{

	...
	switch (wNotifyCode)
	{
	case BN_CLICKED:
		// Fire click event when button is clicked
		FireClick();
		break;
	case BN_KILLFOCUS:
	...

	}
	return 0;
}

工具提示

MSDN 文章 Q141871(HOWTO: Add Tooltips to ActiveX Controls)描述了实现工具提示的步骤。要点:添加 RelayEvent 方法和一个 CToolTipCtrl m_ttip 成员变量;创建并激活工具提示;在 WM_LBUTTONDOWNWM_LBUTTONUPWM_MOUSEMOVE 的处理程序中调用 RelayEvent 以将适当的消息中继给工具提示控件。

悬停功能

您必须使用一个 bool m_tracking 成员变量来跟踪鼠标位置。当鼠标悬停在按钮上时,会发送 WM_MOUSEMOVE 消息,在这里激活跟踪。

   ...
   if (!m_tracking) {
      TRACKMOUSEEVENT t = {sizeof(TRACKMOUSEEVENT),TME_LEAVE,m_hWnd,0};
      if (::_TrackMouseEvent(&t)) {
         m_tracking = true;
         Invalidate();
      }
   }

要检测鼠标何时离开,您必须在控件类的 .H 文件中添加此消息处理程序

LRESULT OnMouseLeave(WPARAM, LPARAM);

并在控件类的 .CPP 文件中

ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)

LRESULT AxButtonCtrl::OnMouseLeave(WPARAM, LPARAM)
{
    ASSERT (m_tracking);
    m_tracking = false;
    Invalidate();
    return 0;
}

结论

本文介绍了如何将所有者绘图按钮转换为 ActiveX 控件,这只是一个开始,要构建一个完整的控件,我还需要讨论自动化、属性表等等……,但这些主题超出了本文的范围。有关 COM 的详细信息,请阅读这些 文章

许可证

本文没有明确的许可证附加到它,但可能包含在文章文本或下载文件本身中的使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.