首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

自繪選單實現 CMenuEx:將所有子選單轉為彈出式選單觸發框架自動繪製

     編輯:關於VC++

早期自繪選單方案需手動映射 WM_DRAWITEM/WM_MEASUREITEM,BCMenu 則宣稱無需映射即可自繪。本文深入分析 MFC 框架機制,發現關鍵在於「子選單必須為彈出式選單(CreatePopupMenu)框架才會自動呼叫 MeasureItem/DrawItem」。據此實作 CMenuEx::LoadMenu 重構整個選單樹,將所有子選單建立為彈出式選單並關聯 CMenuEx 實例,達成零訊息映射自繪。

核心原理

MFC CWnd::OnMeasureItem 內部邏輯:

pMenu = GetMenu(); // 取得主選單
pMenu = _AfxFindPopupMenuFromID(pMenu, itemID); // 尋找對應彈出選單
if (pMenu) pMenu->MeasureItem(lpMIS); // 呼叫虛擬函數

只有彈出式選單(MF_POPUP)才會被 _AfxFindPopupMenuFromID 找到並觸發虛擬函數呼叫。因此 CMenuEx::LoadMenu 採用「讀取資源 → 遞迴重建」策略:

  • 主選單用 CreateMenu。
  • 每個子選單用 CreatePopupMenu 建立,插入父選單時帶 MF_POPUP。
  • 遞迴呼叫 ChangeMenuStyle 將所有項目設為 MF_OWNERDRAW,並將項目資料指標(LPCTSTR)指向自訂 MENUITEM 結構。

關鍵程式碼架構

BOOL CMenuEx::LoadMenu(UINT uMenu) {
    HMENU hMenu = ::CreateMenu(); Attach(hMenu);
    CMenu tmp; tmp.LoadMenu(uMenu);
    for (int i=0; iAttach(hDest); m_SubMenuArr.Add(pNew);
    for (int i=0; iGetMenuItemCount(); ++i) {
        UINT uID = pSrc->GetMenuItemID(i);
        MENUITEM* pItem = new MENUITEM; // 填充文字、ID、圖示索引等
        if (uID == 0) { // 分隔線
            AppendMenu(hDest, MF_SEPARATOR, 0, NULL);
            ModifyMenu(hDest, i, MF_BYPOSITION|MF_OWNERDRAW, -1, (LPCTSTR)pItem);
        } else if ((int)uID == -1) { // 子選單
            // 建立巢狀彈出選單、遞迴
        } else { // 一般項目
            AppendMenu(hDest, MF_OWNERDRAW|MF_BYCOMMAND|state, uID, (LPCTSTR)pItem);
        }
    }
}

擴充介面

  • LoadToolBar(UINT uToolBar, UINT uFace):從工具列資源載入真彩圖示(uFace 為外部編輯的真彩位圖 ID),解決 VC6 難以直接編輯真彩工具列的問題。
  • ModifyMenuEx/AppendMenuEx/RemoveMenuEx:自動處理 MF_OWNERDRAW 風格的選單動態修改介面。

使用步驟

  1. 專案加入 MenuEx.h/.cpp。
  2. 宣告 CMenuEx m_Menu 成員。
  3. m_Menu.LoadMenu(IDR_MENU1) 載入選單資源。
  4. 需圖示時呼叫 m_Menu.LoadToolBar(...)。

注意事項

MeasureItem/DrawItem 應盡量從 MENUITEM 結構取得所有繪製資訊,避免依賴 CMenuEx 成員變數,確保邏輯獨立、可重用。範例程式展示主選單與彈出選單自繪效果,含圖示、選取態、分隔線等完整 UI。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved