Introduction
I was looking for a code to implement an owner-drawn context menu in WTL, but could not find any; everything I found would modify the behavior of the CCommandBarCtrl
, and could not be used in a context menu displayed by, let's say, an edit control.
In this article, I�ll show you how to implement an owner-drawn context menu, and how to add cool-looking menus to your application real easy, using my class CCoolContextMenu
.
Implementation
In order to make an item an owner-drawn item, you must create a new menu item, or modify an existing one by setting the MFT_OWNERDRAW
menu flag.
You can use the InsertMenuItem
or SetMenuItemInfo
functions to set or change information about a menu item. When calling these two functions, you must specify a pointer to a MENUITEMINFO
structure, which specifies the properties of the menu item.
You need to specify the MFT_OWNERDRAW
value for the fType
member, for an item to become an owner-drawn item. You can also associate an application-defined value, which is called item data, with each menu item. The CCoolContextMenu
class defines the MenuItemData
structure that contains the information used to draw a menu item. The application uses the dwItemData
member to store a pointer to this structure.
The MenuItemData
structure is sent to the menu's owner window with the WM_MEASUREITEM
and WM_DRAWITEM
messages. The GetMenuItemInfo
function is used to retrieve the item data for a menu at any time.
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<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);
}
}
}
m_stackMenuHandle.Push(menuPopup.m_hMenu);
return 0;
}
Internally, CCoolContextMenu
implements WM_MEASUREITEM
, WM_DRAWITEM
, WM_INITMENUPOPUP
, and WM_MENUSELECT
messages so you do not have to worry about anything.
Using the code
To use the CCoolContextMenu
class, all you have to do is to derive your class from CCoolContextMenu
and follow the next few steps:
class CCoolEdit : public CWindowImpl<CCoolEdit, CRichEditCtrl>,
public CRichEditCommands<CCoolEdit>,
public CCoolContextMenu<CCoolEdit>
In the message map, use CHAIN_MSG_MAP
to redirect messages to CCoolContextMenu
's message map:
BEGIN_MSG_MAP(CCoolEdit)
...
CHAIN_MSG_MAP(CCoolContextMenu<CCoolEdit>)
END_MSG_MAP()
In the OnInitDialog()
function of your dialog class, or in the OnCreate()
function of the window class, call the GetSystemSettings()
method of CCoolContextMenu
:
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
...
GetSystemSettings();
...
}
But wait! What about images? Don't worry, just create the image list and implement the AssociateImage(CMenuItemInfo& mii, MenuItemData * pMI)
function in your CCoolContextMenu
derived class:
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;
}
}
Conclusion
CCoolContextMenu
allows you to add nice looking context menus to your application with just a few lines of code; and by combining my code with Jean-Michel's, your windows will look completely different.
My Thanks To
History
- March 13, 2006 - Initial release.
- March 15, 2006 - Put a message map in the
CCoolContextMenu
class. Thanks to J�rgen Sigvardsson for suggesting it.
- April 24, 2006 - Added a default implementation of the
AssociateImage
function to the CCoolContextMenu
class.
Disclaimer
THIS SOFTWARE AND THE ACCOMPANYING FILES ARE DISTRIBUTED "AS IS" AND WITHOUT ANY WARRANTIES WHETHER EXPRESSED OR IMPLIED. NO RESPONSIBILITIES FOR POSSIBLE DAMAGES CAN BE TAKEN. THE USER MUST ASSUME THE ENTIRE RISK OF USING THIS SOFTWARE.