Most of us have tried to figure out how
to make a nice menu like MS Office has. Every command menu that has shortcut
in toolbar will display a same bitmap with the toolbar. We don't have, as
far as I know, such API to do that directly. And if we started to build
our own menu with a special window, which very hard because we can't use
the TrackWindowPopup(...)API
and we have to re-implement the whole thing again. Which is not a very
good idea.
Creating a custom menu is not very
difficult in MFC framework. We only need to derive MFC's CMenu
class and override it's CMenu::DrawItem(...)
and CMenu::MeasureItem(...)
functions.
CMenu::DrawItem(...)
will be called by the frame work
every time each menu item will be drawn. So we override this member to
draw our own menu.
CMenu::MeasureItem(...)will
be called by the frame work for to
determine the size of the rectangle for each menu item. We're going to
override this to provide some space for our bitmap.
To display bitmap in our menu, I use CMapStringToPtr
to hold resource id value. Our menu
going to load bitmap dynamically and draw it down to our menu
surface.
Following codes will explain to you how
the whole thing works out. I only put down important part from the whole
codes. I hope I'll have enough space to provide downloadable project.
|
First we have to create a
class from CMenu, lets name our class CMSMenu:
class CMSMenu
: public
CMenu
{
public:
CMSMenu();
virtual ~CMSMenu();
// Our own member function to add
Menu item replace CMenu::InsertMenu(...)
BOOL fInsert( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0,
LPCTSTR lpszNewItem = NULL, UINT nBitmapID =0);
protected:
// Member variable to hold
the Bitmap resource ID
CMapStringToPtr m_Map;
// Two member functions that we
will override from CMenu
virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
virtual void MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct );
};
CMSMenu::CMSMenu()
{
}
CMSMenu::~CMSMenu()
{
m_Map.RemoveAll(); //
Don't forget to
cleanup our map object.
}
BOOL CMSMenu::fInsert( UINT nPosition, UINT nFlags, UINT nIDNewItem,
LPCTSTR lpszNewItem, UINT nBitmapID)
{
m_Map.SetAt(lpszNewItem, (void*)nBitmapID);
return
CMenu::InsertMenu(nPosition, nFlags, nIDNewItem, lpszNewItem);
}
// Overriden DrawItem member
void CMSMenu::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
// We don't want to redraw grayed
menu everytime it gets selected
// so we just return without doing anything.
if(lpDrawItemStruct->itemAction & ODA_SELECT &&
lpDrawItemStruct->itemState &
ODS_DISABLED)
return;
// Construct CDC from lpDrawItem
member
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
// Get item's rectangle
CRect rct = lpDrawItemStruct->rcItem;
// Get String data
CString str;
str = (LPCTSTR) lpDrawItemStruct->itemData;
// Draw the menu's backgroud.
Blue if selected and gray otherwise.
if(lpDrawItemStruct->itemState & ODS_SELECTED &&
!(lpDrawItemStruct->itemState & ODS_DISABLED) )
{
pDC->FillSolidRect(rct,
GetSysColor(COLOR_HIGHLIGHT));
pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else
{
pDC->FillSolidRect(rct,
GetSysColor(COLOR_MENU));
pDC->SetTextColor(GetSysColor(COLOR_MENUTEXT));
}
// Draw String data
CRect rctText = rct;
rctText.OffsetRect(28,0);
pDC->DrawText(str, rctText, DT_SINGLELINE|DT_VCENTER);
//Clear rectangle for bitmap
CRect rctBmp = rct;
rctBmp.right = rctBmp.left + 20;
pDC->FillSolidRect(rctBmp,GetSysColor(COLOR_BTNFACE));
// Get bitmap from resource with
the Resource ID our m_Map member
void* id;
m_Map.Lookup(str, id);
CBitmap bmp;
bmp.LoadBitmap((UINT)id);
if(id !=0 )
{
//Clear rectangle for bitmap
CRect rctBmp = rct;
rctBmp.right = rctBmp.left + 20;
pDC->FillSolidRect(rctBmp,GetSysColor(COLOR_BTNFACE));
CBitmap bmp;
bmp.LoadBitmap((UINT)id);
// Determine the bitmap position
CPoint pnt = rct.TopLeft();
CSize sz = rct.Size();
sz.cx = 16;
sz.cy = 16;
pnt.Offset(2,2);
// Draw bitmap
pDC->DrawState(pnt,sz, &bmp, DST_BITMAP |
DSS_NORMAL);
bmp.DeleteObject();
// Draw Rectangle around bitmap if selected
if(lpDrawItemStruct->itemState & ODS_SELECTED &&
!(lpDrawItemStruct->itemState &
ODS_DISABLED))
pDC->DrawEdge(rctBmp,
BDR_RAISEDINNER, BF_RECT);
}
// If the item menu is disabled,
draw it gray.
if(lpDrawItemStruct->itemState & ODS_DISABLED)
fDrawDisabled(*pDC, rct);
CMenu::DrawItem(lpDrawItemStruct);
}
// Overriden MeasureItem
member
void CMSMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct )
{
CString str = (LPCTSTR) lpMeasureItemStruct->itemData;
CRect rct(0,0,0,0);
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
// Get item rectangle by
calculating item's text rectangle
pDC->DrawText(str, rct, DT_CALCRECT|DT_SINGLELINE);
// Get some space
lpMeasureItemStruct->itemHeight = rct.Height()+4;
lpMeasureItemStruct->itemWidth = rct.Width()+ 4;
CMenu::MeasureItem(lpMeasureItemStruct);
}
|