一些能让您的 ATL/WTL 程序拥有更好外观的 Mix-in 类






2.69/5 (5投票s)
2004年4月2日
6分钟阅读

54839

1042
本文介绍了一些用于 WTL 的 Mix-In 类。这些 Mix-In 类提供了 WTL/ATL 框架不支持的一些功能,例如拥有绘制菜单、皮肤化对话框按钮和静态文本控件。
目录
引言
我是一名 MFC 程序员,MFC 是一个很好的框架。但 MFC 在计划和开发小型内存驻留程序方面也有一些缺点。对于这类程序,我们需要程序体积小、占用资源少且没有资源泄漏。显然,这不是 MFC 的优势,所以我开始研究 WTL。与 MFC 不同,WTL 没有内置支持拥有绘制菜单。我编写了 CMenuHelp
类来为普通的 WTL 窗口提供菜单皮肤功能。CWzButtonImpl
是一个简单的拥有绘制按钮类,它与 CButtonHelp
配合,后者使用 CWzButtonImpl
来子类化对话框中的所有按钮。CButtonHelp
也可以与其他拥有绘制按钮类配合。CCtrlColor
处理对话框窗口的 WM_CTLCOLORDLG
和 WM_CTLCOLORSTATIC
消息,使用模板参数更改对话框的背景色和控件的文本颜色。
由于我不是 WTL/ATL 框架的专家,不能保证代码对每个人都有效。欢迎任何建议。您可能会注意到本文档存在许多语法错误,甚至有时完全错误,这是因为我不是英语母语者。希望大家都能理解这篇文章。
CMenuHelp
众所周知,当菜单弹出时,Windows 系统会向菜单的所有者窗口发送 WM_INITMENU
和 WM_ENTERMENULOOP
消息,当菜单消失时,Windows 系统会向所有者窗口发送 WM_EXITMENULOOP
消息。如果菜单项具有 MFT_OWNERDRAW
属性,Windows 还会发送 WM_MEASUREITEM
来测量菜单项,并发送 WM_DRAWITEM
消息让所有者窗口绘制菜单项。
CMenuHelp
是一个模板类,也是一个 MixIn 类。它处理所有者窗口的 5 个消息。在 WM_INITMENU
消息处理中,它获取活动菜单的句柄(菜单尚未显示)并调用 AttachActiveMenu()
为该菜单中的所有菜单项添加 MFT_OWNERDRAW
属性。当菜单消失时,会调用 DetachActiveMenu()
,此方法会移除 AttachActiveMenu()
添加的 MFT_OWNERDRAW
属性。
CMenuHelp 的属性
HMENU CMenuHelp::m_hActiveMenu
存储弹出菜单的句柄,
OnMeasureItem
和OnDrawItem
使用此句柄对弹出菜单执行某些操作。COLORREF CMenuHelp::m_crText
用于绘制普通菜单项文本的颜色。
COLORREF CMenuHelp::m_crHiText
用于绘制选中菜单项文本的颜色。
COLORREF CMenuHelp::m_crBkGnd
用于绘制菜单项背景的颜色。
COLORREF CMenuHelp::m_crHiBkGnd
用于绘制选中菜单项背景的颜色。
CMenuHelp 的方法
HMENU CMenuHelp::AttachActiveMenu(HMENU hActiveMenu)
将将要弹出的菜单句柄附加到
m_hActiveMenu
。hActiveMenu
是新菜单的句柄。AttachActiveMenu()
为该菜单中的所有菜单项添加MFT_OWNERDRAW
属性,因此系统会向我们的窗口发送WM_MEASUREITEM
和WM_DRAWITEM
消息。CMenuHelp
处理这两个消息来绘制菜单。AttachActiveMenu()
返回上一个弹出菜单的句柄。HMENU CMenuHelp::DetachActiveMenu()
从
m_hActiveMenu
的所有菜单项中移除MFT_OWNERDRAW
属性并将m_hActiveMenu
设置为NULL
。它返回当前弹出菜单的句柄。
在您的代码中使用 CMenuHelp
在代码中使用此类非常简单。将 MenuHelp.h 包含到您的项目中,将 CMenuHelp
添加到您窗口的派生链中,如下所示:
class CMainDlg : public CDialogImpl<CMainDlg>,public CMenuHelp<CMainDlg>
最后,将声明信息添加到消息链中:
BEGIN_MSG_MAP(CMainDlg) ... CHAIN_MSG_MAP(CMenuHelp<CMainDlg>) ... END_MSG_MAP()
当它工作时,该窗口的所有弹出菜单都将由我们的 CMenuHelp
绘制,而不是 Windows 的普通菜单外观。
CButtonHelp
CButtonHelp
是一个用于更改对话框中按钮控件外观的辅助类。它在对话框初始化自身时修改并将 BS_OWNERDRAW
属性添加到所有推送按钮,并将这些按钮控件链接到 CWzButtonImpl
类。CWzButtonImpl
是真正的拥有绘制按钮类。CWzButtonImpl
使用 SubclassWindow
来接管标准 Windows 按钮的测量和绘制行为,就像大多数其他 MFC 的 CButton
派生类一样。
CWzButtonImpl
CWzButtonImpl
是一个派生自 CButton
(ATL 类) 的拥有绘制按钮类。它易于使用,就像大多数 MFC 的拥有绘制按钮类一样。首先声明一个 CWzButtonImpl
对象,然后调用 SubclassWindow
方法将其附加到推送按钮控件。如果您不喜欢当前的样式,可以在此类中修改绘制代码来更改按钮控件的外观。您也可以用其他拥有绘制按钮类替换此类。您可以在 CodeProject 中找到很多。您也可以在应用程序中指定几种不同的拥有绘制按钮类(样式),但一个对话框中只能使用一个类。
CButtonHelp 的属性
CSimpleArray<t_ButtonClass *> CButtonHelp::m_arButtons
存储对话框中的所有
t_ButtonClass
对象,每个t_ButtonClass
对象都附加到一个推送按钮。t_ButtonClass
是一个模板参数,在我们的示例代码中应为CWzButtonImpl
。
CButtonHelp 的方法
BOOL CButtonHelp::SubclassAllButtons()
将对话框中的推送按钮附加到
t_ButtonClass
类对象。通常在窗口的初始化函数中调用,例如CDialog::OnInitDialog
。BOOL CButtonHelp::SubclassButton(HWND hBtnWnd)
创建一个
t_ButtonClass
对象并将其附加到普通推送按钮控件。hBtnWnd
是按钮控件的HWND
。BOOL CButtonHelp::UnSubclassButton(HWND hBtnWnd)
从推送按钮控件分离
t_ButtonClass
对象。hBtnWnd
是按钮控件的HWND
。t_ButtonClass
对象将被从CMenuHelp::m_arButtons
中删除。int CButtonHelp::FindButtons(HWND hBtnWnd)
用于确定一个推送按钮控件是否已经被子类化。
在您的代码中使用 CButtonHelp
首先将 ButtonHelp.h 包含到您的项目中,然后将 CButtonHelp
类添加到对话框的派生链中,如下所示:
class CMainDlg : public CDialogImpl<CMainDlg>, public CButtonHelp<CMainDlg,CWzButtonImpl>
最后,别忘了在 CDialog::OnInitDialog
中调用 SubclassAllButtons()
。
CCtrlColor
CCtrlColor
类处理一些 WM_CTLCOLOR*
消息(目前仅支持 WM_CTLCOLORDLG
和 WM_CTLCOLORSTATIC
),并使用特殊的文本颜色和背景颜色(由模板参数指定)绘制对话框中的控件。这是一个非常简单的类,只有 34 行 C++ 代码。
CCtrlColor 的属性
HBRUSH CCtrlColor::m_brBkgnd
HBRUSH
GDI 对象:CCtrlColor
类使用此画笔绘制对话框和控件的背景。CCtrlColor
在构造函数中创建此对象,使用模板参数t_crBkgnd
指定的颜色,并在析构函数中删除此对象。
在您的代码中使用 CCtrlColor
CCtrlColor
有三个模板参数:T
、t_crTextColor
和 t_crBkgnd
。后两个是可选的,它们默认为 RGB(64,64,255) 和 RGB(222,222,222)。因此,您可以通过两种方法将 CCtrlColor
添加到对话框的派生链中:
class CMainDlg : public CDialogImpl<CMainDlg>, public CCtrlColor<CMainDlg>
或
class CAboutDlg : public CDialogImpl<CAboutDlg>, public CCtrlColor<CAboutDlg,RGB(64,128,64)>
与 CMenuHelp
相同,将声明信息添加到消息链中:
BEGIN_MSG_MAP(CMainDlg) ... CHAIN_MSG_MAP(CCtrlColor<CMainDlg>) ... END_MSG_MAP()
关于演示代码
所有源代码都组织在一个 VC6 项目中,未在 VC7 编译器中测试。由于本文档讨论的是 ATL/ATL,您还应该安装 WTL 来编译源代码。此代码的使用没有限制,可用于任何目的。