在VCKBASE上讀到《一種漂亮的自繪菜單》 [作者:鄭恆 (lbird)]。應用到我的工程裡後發現:文章中提到的效果能很好的實現。但是有一點不方便:需要映射 WM_DRAWITEM 和 WM_MEASUREITEM 消息才能實現自畫功能。這對於一個基於對話框的工程或者僅僅需要彈出式菜單的工程來說很不方便。網上有一種很有名的自繪菜單 :BCMenu (http://www.rocscience.com/~corkum/BCMenu.html) (在附帶工程中也有 BCMenu),在使用它的時候並不需要映射上述的兩個消息就能實現自繪效果。這個問題讓我覺得很困惑,MSDN也說明:MeasureItem() 和 DrawItem() 兩個虛函數是由框架調用的 。並不用手工映射。可是若不映射上述的兩個消息則顯示不正常。(我查看了好多資料,直到現在還是不明白原因。呵呵:))既然 BCMenu 可以不用映射 WM_DRAWITEM 和 WM_MEASUREITEM 就能實現自畫功能,那麼它肯定經過了特殊處理。果然,BCMenu::LoadMenu()對整個菜單作了處理 。我注意到,如果菜單是彈出式的,那麼不需要映射 WM_DRAWITEM 和 WM_MEASUREITEM 就能實現自畫功能。於是我在CMenuEx::LoadMenu()中重新構建了整個菜單, 把所有的子菜單創建為彈出式的菜單使用API函數::CreatePopupMenu(),代碼如下:
BOOL CMenuEx::LoadMenu(UINT uMenu) { //重新讀入菜單,創建為popup菜單,才能自畫(由框架調用MesureItem() 和 DrawItem() HMENU hMenu = ::CreateMenu(); this->Attach(hMenu); //臨時菜單(使用CMenu的LoadMenu()函數讀入菜單,並以之為藍本構建新的菜單) CMenu Menu; UINT uID; Menu.LoadMenu(uMenu); for(int i = 0; i < (int)Menu.GetMenuItemCount(); i++) { uID = Menu.GetMenuItemID(i); if(uID == 0) //分隔符 { ::AppendMenu(hMenu,MF_SEPARATOR,0,NULL); } else if((int)uID == -1) //彈出菜單(即子菜單) { CMenu *pSubMenu = Menu.GetSubMenu(i); //創建子菜單 HMENU hSubMenu = ::CreatePopupMenu(); CString strPopup; Menu.GetMenuString(i,strPopup,MF_BYPOSITION); ::InsertMenu(hMenu, i, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT)hSubMenu, strPopup); //對子菜單遞歸調用ChangeMenuStyle(),把子菜單改為MF_OWNERDRAW風格 ChangeMenuStyle(pSubMenu,hSubMenu); } else //正常的菜單項 { CString strText; Menu.GetMenuString(uID,strText,MF_BYCOMMAND); AppendMenu(MF_STRING,uID,strText); } } Menu.DestroyMenu(); //銷毀臨時菜單 return TRUE; } void CMenuEx::ChangeMenuStyle(CMenu *pMenu,HMENU hNewMenu) { //關聯為CMenuEx(關聯為CMenuEx後才能自動重畫 //原因不明(CMenu封裝的結果?) CMenuEx *pNewMenu; pNewMenu = new CMenuEx; pNewMenu->Attach(hNewMenu); m_SubMenuArr.Add(pNewMenu); UINT uID; int nItemCount = pMenu->GetMenuItemCount(); for(int i = 0; i < nItemCount; i++) { uID = pMenu->GetMenuItemID(i); if(uID == 0) //分隔符 { ::AppendMenu(hNewMenu,MF_SEPARATOR,0,NULL); //pNewMenu->AppendMenu(MF_SEPARATOR,0,NULL); CString strText; MENUITEM *pMenuItem = new MENUITEM; pMenuItem->uID = 0; pMenuItem->uIndex = -1; pMenuItem->uPositionImageLeft = -1; pMenuItem->pImageList = &m_ImageList; m_MenuItemArr.Add(pMenuItem); ::ModifyMenu(hNewMenu, i, MF_BYPOSITION | MF_OWNERDRAW, -1, (LPCTSTR)pMenuItem); } else if(uID == -1) //彈出菜單(即子菜單) { CMenu *pSubMenu = pMenu->GetSubMenu(i); HMENU hPopMenu = ::CreatePopupMenu(); CString strPopup; pMenu->GetMenuString(i,strPopup,MF_BYPOSITION); ::InsertMenu(hNewMenu, i, MF_BYPOSITION | MF_POPUP, (UINT)hPopMenu, strPopup); MENUITEM *pMenuItem = new MENUITEM; pMenuItem->uID = -1; pMenuItem->strText = strPopup; pMenuItem->uIndex = -1; pMenuItem->uPositionImageLeft = -1; pMenuItem->pImageList = &m_ImageList; m_MenuItemArr.Add(pMenuItem); ::ModifyMenu(hNewMenu, i, MF_BYPOSITION | MF_OWNERDRAW, -1, (LPCTSTR)pMenuItem); ChangeMenuStyle(pSubMenu,hPopMenu); } else //正常的菜單項 { CString strText; pMenu->GetMenuString(uID,strText,MF_BYCOMMAND); MENUITEM *pMenuItem = new MENUITEM; pMenuItem->uID = pMenu->GetMenuItemID(i); pMenu->GetMenuString(pMenuItem->uID, pMenuItem->strText, MF_BYCOMMAND); pMenuItem->uIndex = -1; pMenuItem->uPositionImageLeft = -1; pMenuItem->pImageList = &m_ImageList; m_MenuItemArr.Add(pMenuItem); UINT uState = pMenu->GetMenuState(i,MF_BYPOSITION); ::AppendMenu(hNewMenu, MF_OWNERDRAW | MF_BYCOMMAND | uState, uID, (LPCTSTR)pMenuItem); } } }
這樣,利用標注的CMenu::LoadMenu()函數讀入菜單,並根據這個菜單重新構建一個新的菜單,在新菜單中把所有的子菜單創建為彈出式菜單並關聯一個CMenuEx類。根據需要,我提供了一個
本文示例代碼或素材下載