一、提出問題
在VCKBASE上讀到《自繪菜單的實現》[作者:querw]。應用的我自己的正在進行的工程後發現效果不錯,可是有存在許多問題。整個類的設計方面存在很多缺陷(先天,後天的),存在的主要問題如下:
菜單編輯器中的模菜單樣
使用BCMENU並且映射了這兩個消息後的執行情況
使用BCMENU沒有映射兩個消息的執行情況
原作者分析的自繪的是因為把主菜單(top-level menu)的子菜單都加載成彈出菜單(popupmenu),是不正確的。真正的原因是因為MFC框架會自動調用CMenu的兩個虛擬函數MeasureItem()和OnDrawItem()。 因此,當CMenuEx派生於CMenu,並且重寫這兩個虛擬函數以後。
1、MFC框架調用的GetMenu()->MeasureItem()就相當於調用了CMenuEx::MeasureItem(),從而實現自繪菜單控件尺寸的測量。2、MFC框架調用GetMenu()->DrawItem()就相當於調用了CMenuEx::DrawItem()來實現自繪菜單控件的自繪操作(不懂??,這正是C++的虛擬的妙用,指向派生類對象的基類指針可以調用派生類的虛擬函數,多麼偉大的發明,誰想出來的???)。與子菜單是否為彈出菜單(popupmenu)沒有什麼關系。以下是摘自WINCORE.CPP的一段程序,也就是WM_MEASUREITEM消息的默認流向的地方,相信大家會從中看出一些端倪。
void CWnd::OnMeasureItem(int /*nIDCtl*/, LPMEASUREITEMSTRUCT lpMeasureItemStruct) { if (lpMeasureItemStruct->CtlType == ODT_MENU) { ...... // 如果沒有主菜單 if (pThreadState->m_hTrackingWindow == m_hWnd) { ...... } else { // 如果有主菜單 pMenu = GetMenu(); // 找到窗體的主菜單,注意,pMenu的是CMenu* 類型 } // 在當前菜單中尋找ID匹配的菜單項 pMenu = _AfxFindPopupMenuFromID(pMenu, lpMeasureItemStruct->itemID); if (pMenu != NULL) // 如果找到,就調用MeasureItem() // 這就是所謂的基類指針指向派生類對象,可以調用派生類虛擬函數的情況了 pMenu->MeasureItem(lpMeasureItemStruct); else TRACE1("Warning: unknown WM_MEASUREITEM for menu item 0x%04X.n", lpMeasureItemStruct->itemID); } else { ...... } ...... }
摘錄自原CMenuEx.cpp第546-560行
if(uID == 0) //分隔符 { ::AppendMenu(hNewMenu,MF_SEPARATOR,0,NULL); ...... // 注意,就是下面那個-1,把分割條的ID從0改到-1, // 從而是MFC框架誤以為找到了ID為-1的菜單項,並且測量了它的尺寸 // 而實際上ID為-1的菜單項是不可能被void CWnd::OnMeasureItem()找到的 ::ModifyMenu(hNewMenu,i,MF_BYPOSITION | MF_OWNERDRAW,-1,(LPCTSTR)pMenuItem); }菜單編輯器中沒有分割條菜單的菜單
原CMenuEx執行的模樣
本文示例代碼或素材下載