菜单自绘分为两类,一类为无边框,一类为有边框,无边框是通过hook实现的,有边框菜单:
实现菜单自绘原理很简单:
1.给每一项都加上MF_OWNERDRAW属性
实现代码:void CMyMenu::ChangeToOwnerDraw(CMyMenu *pMyMenu) { CString str; CMyMenu* pMenu; int iMenuCount = pMyMenu->GetMenuItemCount(); UINT nID; for (int i=0; i<iMenuCount; i++) { pMyMenu->GetMenuString(i, str, MF_BYPOSITION); pMenu = 0; if (pMyMenu->GetSubMenu(i)) { pMyMenu->ModifyMenu(i, MF_BYPOSITION | MF_OWNERDRAW, 0,(LPCTSTR)str); pMenu = new CMyMenu; HMENU hMenu = pMyMenu->GetSubMenu(i)->GetSafeHmenu(); pMenu->Attach(hMenu); m_pMenuList.push_back(pMenu); ChangeToOwnerDraw(pMenu); } else { nID = pMyMenu->GetMenuItemID(i); pMyMenu->ModifyMenu(i, MF_BYPOSITION | MF_OWNERDRAW, (UINT)nID,(LPCTSTR)str); } } }
这里如果有二级菜单,就要把new出来的menu最后释放掉,我用了个pstd::vector<CMyMenu*> m_pMenuList ;来存储,最后在析构函数中统一释放掉
2.重载MeasureItem确定菜单项的宽度和高度
实现代码:void CMyMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { lpMeasureItemStruct->itemHeight =m_MenuHight; lpMeasureItemStruct->itemWidth=m_MenuWidth; }
3.重载DrawItem自绘菜单
实现代码:
void CMyMenu::DrawItem(LPDRAWITEMSTRUCT lpDIS) { CDC* pDC = CDC::FromHandle(lpDIS->hDC); CRect rc(lpDIS->rcItem); CRect rcBgFrameLeft; rcBgFrameLeft.SetRect(rc.left,rc.top,rc.left+1,rc.bottom); CRect rcBgFrameRight; rcBgFrameRight.SetRect(rc.right-1,rc.top,rc.right,rc.bottom); CRect rcIsSelected; rcIsSelected.SetRect(rc.left+1,rc.top+1,rc.right-1,rc.bottom-1); CRect rcBgTop; rcBgTop.SetRect(rc.left,rc.top,rc.right,rc.top+1); CRect rcBgBottom ; rcBgBottom.SetRect(rc.left,rc.bottom-1,rc.right+1,rc.bottom) ; pDC->FillSolidRect(&rcBgFrameLeft,RGB(159,160,157)); pDC->FillSolidRect(&rcBgFrameRight,RGB(159,160,157)); pDC->FillSolidRect(&rcBgTop,RGB(169,169,169)); pDC->FillSolidRect(&rcBgBottom,RGB(169,169,169)); CString str; GetMenuString(lpDIS->itemID,str,MF_BYCOMMAND); //str = (LPCTSTR)lpDIS->itemData; CFont *pOldFont=pDC->SelectObject(&font); if ((lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE))) { UINT nFlags = GetMenuState(lpDIS->itemID,MF_BYCOMMAND) ; if (nFlags&MF_GRAYED) { //..... } else if (nFlags&MF_DISABLED) { pDC->FillSolidRect(&rcIsSelected,RGB(188,237,79)); pDC->SetTextColor(RGB(182,183,182)); pDC->SetBkMode(TRANSPARENT); pDC->DrawText(str,&rcIsSelected,DT_VCENTER|DT_SINGLELINE|DT_CENTER); } else { pDC->FillSolidRect(&rcIsSelected,RGB(188,237,79)); pDC->SetTextColor(RGB(120,167,47)); pDC->SetBkMode(TRANSPARENT); pDC->DrawText(str,&rcIsSelected,DT_VCENTER|DT_SINGLELINE|DT_CENTER); } } else { UINT nFlags = GetMenuState(lpDIS->itemID,MF_BYCOMMAND) ; if ((nFlags&MF_GRAYED)|(nFlags&MF_DISABLED)) { pDC->FillSolidRect(&rcIsSelected,RGB(224,224,224)); pDC->SetTextColor(RGB(182,183,182)); pDC->SetBkMode(TRANSPARENT); pDC->DrawText(str,&rcIsSelected,DT_VCENTER|DT_SINGLELINE|DT_CENTER); } else { pDC->FillSolidRect(&rcIsSelected,RGB(224,224,224)); pDC->SetTextColor(RGB(138,157,119)); pDC->SetBkMode(TRANSPARENT); pDC->DrawText(str,&rcIsSelected,DT_VCENTER|DT_SINGLELINE|DT_CENTER); } } }
注意:如果你不想调用ChangeToOwnerDraw来统一添加MF_OWNERDRAW,你可以直接在AppendMenu时就直接加入这一属性,
如:m_pMenu->AppendMenu( MF_STRING|MF_OWNERDRAW,IDM_PHONE_PUP0,_T("0000000"));
如果你发现不能显示字体,请把DrawItem中的GetMenuString(lpDIS->itemID,str,MF_BYCOMMAND);用str = (LPCTSTR)lpDIS->itemData;代替.
下面说说怎么去掉边框,
在App中加入类IntMenu,在应用程序创建前调用
IntMenu menu ;
menu.UnInstallHook();
menu.InstallHook();
menu.Lapha=0;
即可
本人小菜,随手写的,很多不周,别拍砖,3Q。
====〉源代码下载