針對現有自繪選單方案(如 BCMenu、CMenuEx 早期版本)在 MDI 環境下無法處理系統自動插入的選單(如「最近檔案」MRU 清單)、子選單高度不可調、程式碼結構混亂等痛點,本文重構設計 CMenuEx 類別,提供生產級真彩自繪選單完整解決方案。
CMenu,覆寫 MeasureItem/DrawItem,利用 MFC 框架自動呼叫虛擬函數特性,無需手動映射 WM_MEASUREITEM/WM_DRAWITEM。MENUITEM 結構完整封裝單一選單項目資訊(文字、ID、圖示索引、子選單指標、啟用狀態等),繪製與測量邏輯完全解耦。MeasureItem(LPMEASUREITEMSTRUCT):統一處理所有選單項目尺寸測量。InitPopupMenu(CMenu*, UINT, BOOL):在 OnInitMenuPopup 中呼叫,自動將系統動態插入的選單項目(如 MRU)轉為 Owner-draw。UpdatePopupMenu():同步更新非自繪建立的彈出選單項目。MenuEx.h/.cpp,CMainFrame 宣告兩個 CMenuEx 成員(主選單、文件模板選單)與三組 CImageList(正常/停用/熱點狀態圖示)。nMenuImageIndex[],對應命令 ID。InitMenuImage() 載入真彩位圖(ILC_COLOR24)建立三組圖像清單。InitMainFrameMenu()/InitImageTypeMenu() 分別載入主選單與文件模板選單,設定圖像清單與索引表,回傳 Detach() 後的 HMENU。OnInitMenuPopup 與 OnMeasureItem,鏈結基類實作後呼叫 CMenuEx 靜態函數。CXXXApp::InitInstance() 中建立文件模板時,指定 m_hMenuShared = pMainFrame->InitImageTypeMenu();主框架 m_hMenuDefault = pMainFrame->InitMainFrameMenu();最後呼叫 OnUpdateFrameMenu 同步。// 主框架建立選單
HMENU CMainFrame::InitMainFrameMenu() {
m_menuMainFrame.LoadMenu(IDR_MAINFRAME);
m_menuMainFrame.SetImageList(&m_imageMenu);
m_menuMainFrame.SetDisabledImageList(&m_imageMenuDisable);
m_menuMainFrame.SetHotImageList(&m_imageMenuHot);
m_menuMainFrame.SetImageIndex(nMenuImageIndex, _countof(nMenuImageIndex));
return m_menuMainFrame.Detach();
}
// 訊息映射
void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) {
CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu); // 先讓基類處理(含 MRU 插入)
CMenuEx::InitPopupMenu(pPopupMenu, nIndex, bSysMenu); // 再轉為自繪
}
void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMIS) {
CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMIS);
CMenuEx::MeasureItem(lpMIS);
}
支援兩種模式:
SetImageIndex 批次指派。LoadToolBar(CToolBar*) 直接從已建立的真彩工具列同步圖示與索引(工具列真彩建法參考《如何讓工具列顯示 256 色圖像》)。範例程式展示:小圖示選單(索引表模式)、大圖示選單(工具列模式),均呈現 24-bit 真彩色彩、選取漸層、停用灰階、左側圖示區等現代化 UI 風格。
原始碼結構清晰、註解詳盡,歡迎開發者下載修改、整合至專案,打造專業級選單體驗。