在Windows应用程序开发中,实现一个自定义的右键菜单是一个常见的需求。然而,标准的Win32 API或WTL(Windows Template Library)并没有直接提供现成的解决方案来创建一个完全自定义的右键菜单。本文将介绍如何使用一个名为CCoolContextMenu的类来实现这一功能。
为了创建一个自定义的菜单项,需要设置菜单项的MFT_OWNERDRAW标志。这可以通过InsertMenuItem或SetMenuItemInfo函数来实现。这两个函数都需要一个指向MENUITEMINFO结构的指针,该结构定义了菜单项的属性。
要将一个菜单项设置为自定义绘制(owner-drawn),需要在MENUITEMINFO结构的fType成员中指定MFT_OWNERDRAW值。此外,还可以为每个菜单项关联一个应用程序定义的值,称为项数据。CCoolContextMenu类定义了一个MenuItemData结构,其中包含了用于绘制菜单项的信息。应用程序使用dwItemData成员来存储指向此结构的指针。
MenuItemData结构会随着WM_MEASUREITEM和WM_DRAWITEM消息一起发送给菜单的所有者窗口。GetMenuItemInfo函数用于在任何时候检索菜单项的数据。
以下是初始化菜单弹出处理的代码示例:
LRESULT InitMenuPopupHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
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)) {
MenuItemData *pMI = new MenuItemData;
ATLASSERT(pMI != NULL);
if (pMI) {
mii.fType |= MFT_OWNERDRAW;
pMI->fType = mii.fType;
pMI->fState = mii.fState;
static_cast(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);
}
}
}
m_stackMenuHandle.Push(menuPopup.m_hMenu);
return 0;
}
CCoolContextMenu类内部实现了WM_MEASUREITEM、WM_DRAWITEM、WM_INITMENUPOPUP和WM_MENUSELECT消息,所以不需要担心这些消息的处理。
要使用CCoolContextMenu类,只需要从CCoolContextMenu派生类,并按照以下步骤操作:
class CCoolEdit : public CWindowImpl, public CRichEditCommands, public CCoolContextMenu {
public:
// ...
};
BEGIN_MSG_MAP(CCoolEdit)
// ...
CHAIN_MSG_MAP(CCoolContextMenu)
END_MSG_MAP()
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
// ...
GetSystemSettings();
// ...
}
关于图像,只需要创建图像列表并在CCoolContextMenu派生类中实现AssociateImage函数:
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 LE FOL的代码,窗口将看起来完全不同。