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






4.87/5 (35投票s)
2002年4月25日
5分钟阅读

302144

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_DISABLE
、BN_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()
并实现成员函数来处理反射的消息。wParam
和 lParam
参数与原始窗口消息的参数相同。
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_LBUTTONDOWN
、WM_LBUTTONUP
和 WM_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 的详细信息,请阅读这些 文章。
许可证
本文没有明确的许可证附加到它,但可能包含在文章文本或下载文件本身中的使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。