WTL 中的所有者绘制上下文菜单






4.71/5 (21投票s)
一篇解释如何创建 owner-drawn 上下文菜单的文章。
引言
我一直在寻找在 WTL 中实现 owner-drawn 上下文菜单的代码,但找不到任何代码;我找到的所有代码都会修改 CCommandBarCtrl
的行为,并且不能用于例如由编辑控件显示的上下文菜单。
在本文中,我将向您展示如何实现 owner-drawn 上下文菜单,以及如何使用我的类 CCoolContextMenu
轻松地为您的应用程序添加美观的菜单。
实现
为了使一个项目成为 owner-drawn 项目,您必须创建一个新的菜单项,或通过设置 MFT_OWNERDRAW
菜单标志来修改现有的菜单项。
您可以使用 InsertMenuItem
或 SetMenuItemInfo
函数来设置或更改有关菜单项的信息。在调用这两个函数时,您必须指定一个指向 MENUITEMINFO
结构的指针,该结构指定了菜单项的属性。
您需要为 fType
成员指定 MFT_OWNERDRAW
值,以便将项目变成 owner-drawn 项目。您还可以将一个应用程序定义的值(称为项目数据)与每个菜单项关联起来。CCoolContextMenu
类定义了 MenuItemData
结构,其中包含用于绘制菜单项的信息。应用程序使用 dwItemData
成员来存储指向此结构的指针。
MenuItemData
结构通过 WM_MEASUREITEM
和 WM_DRAWITEM
消息发送到菜单的所有者窗口。GetMenuItemInfo
函数用于随时检索菜单的项目数据。
LRESULT InitMenuPopupHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // System menu, do nothing if ((BOOL)HIWORD(lParam)) { bHandled = FALSE; return 1; } CMenuHandle menuPopup = (HMENU)wParam; ATLASSERT(menuPopup.m_hMenu != NULL); TCHAR szString[MAX_MENU_ITEM_TEXT_LENGTH]; BOOL bRet = FALSE; for (int i = 0; i < menuPopup.GetMenuItemCount(); i++) { CMenuItemInfo mii; mii.cch = MAX_MENU_ITEM_TEXT_LENGTH; mii.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_ID | MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE; mii.dwTypeData = szString; bRet = menuPopup.GetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); if (!(mii.fType & MFT_OWNERDRAW)) // not already an ownerdraw item { MenuItemData * pMI = new MenuItemData; ATLASSERT(pMI != NULL); if (pMI) { // Make this menu item an owner-drawn mii.fType |= MFT_OWNERDRAW; pMI->fType = mii.fType; pMI->fState = mii.fState; // Associate an image with a menu item static_cast<T*>(this)->AssociateImage(mii, pMI); pMI->lpstrText = new TCHAR[lstrlen(szString) + 1]; ATLASSERT(pMI->lpstrText != NULL); if (pMI->lpstrText != NULL) lstrcpy(pMI->lpstrText, szString); mii.dwItemData = (ULONG_PTR)pMI; bRet = menuPopup.SetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); } } } // Add it to the list m_stackMenuHandle.Push(menuPopup.m_hMenu); return 0; }
在内部,CCoolContextMenu
实现了 WM_MEASUREITEM
、WM_DRAWITEM
、WM_INITMENUPOPUP
和 WM_MENUSELECT
消息,因此您不必担心任何事情。
使用代码
要使用 CCoolContextMenu
类,您所要做的就是从 CCoolContextMenu
派生您的类,并按照接下来的几个步骤操作
class CCoolEdit : public CWindowImpl<CCoolEdit, CRichEditCtrl>, public CRichEditCommands<CCoolEdit>, public CCoolContextMenu<CCoolEdit>
在消息映射中,使用 CHAIN_MSG_MAP
将消息重定向到 CCoolContextMenu
的消息映射
BEGIN_MSG_MAP(CCoolEdit) ... CHAIN_MSG_MAP(CCoolContextMenu<CCoolEdit>) END_MSG_MAP()
在您的对话框类的 OnInitDialog()
函数中,或在窗口类的 OnCreate()
函数中,调用 CCoolContextMenu
的 GetSystemSettings()
方法
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { ... GetSystemSettings(); ... }
但是等等!图片呢?别担心,只需创建图像列表并在您的 CCoolContextMenu
派生类中实现 AssociateImage(CMenuItemInfo& mii, MenuItemData * pMI)
函数
void AssociateImage(CMenuItemInfo& mii, MenuItemData * pMI) { switch (mii.wID) { case ID_EDIT_UNDO: pMI->iImage = 17; break; case ID_EDIT_CUT: pMI->iImage = 3; break; case ID_EDIT_COPY: pMI->iImage = 4; break; case ID_EDIT_PASTE: pMI->iImage = 5; break; case ID_EDIT_CLEAR: pMI->iImage = 20; break; default: pMI->iImage = -1; break; } }
结论
CCoolContextMenu
允许您只需几行代码即可为您的应用程序添加美观的上下文菜单;并且通过将我的代码与 Jean-Michel 的代码相结合,您的窗口将看起来完全不同。
感谢
- Jean-Michel LE FOL 提供的 CMenuXP - OfficeXP 菜单 (WTL 版本) 中的菜单自定义代码[^]
- Do Young KIM 提供的 XP 风格 - WTL 中的 MainFrame 中的位图[^]
- Bjarke Viksøe 和 Justin Tay 在 WTL Group 中回答了我的问题[^]
历史
- 2006 年 3 月 13 日 - 初始发布。
- 2006 年 3 月 15 日 - 在
CCoolContextMenu
类中放入一个消息映射。感谢 Jörgen Sigvardsson 建议它。 - 2006 年 4 月 24 日 - 为
CCoolContextMenu
类添加了AssociateImage
函数的默认实现。
免责声明
本软件及其附带的文件按“原样”分发,不提供任何形式的保证,无论是明示的还是暗示的。对于可能造成的任何损害,概不负责。用户必须承担使用本软件的全部风险。