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

完美實現真彩自繪菜單:從 MFC 機制到 CMenuEx 實戰

     編輯:VC 教學

前言:自繪菜單的挑戰與機遇

在 VC++ 開發中,自繪菜單一直是打造專業級界面的關鍵技術。早期開發者常參考《自繪菜單的實現》(作者 querw),但在實際專案中會發現該方案存在諸多缺陷:無法處理 MDI 架構下系統自動添加的菜單(如「最近使用檔案」列表)、對 WM_DRAWITEM 與 WM_MEASUREITEM 訊息映射的理解有誤、子菜單高度計算採用「欺騙 MFC」的 Hack 手段等。本文將深入剖析 MFC 菜單繪製的核心機制,並提供一個健壯的 CMenuEx 實現方案。

核心問題剖析

1. MDI 環境下的系統菜單自繪失效

原類別未處理 CMainFrame::OnInitPopupMenu(),導致無法攔截系統動態插入的菜單項(如 MRU 列表)。BCMenu 雖支援此功能,但其原理常被誤解。

2. 訊息映射與虛擬函數的真相

作者聲稱 BCMenu 不需映射 WM_DRAWITEM 和 WM_MEASUREITEM 即可自繪,這是錯誤的。實際上,MFC 框架會自動呼叫 CMenu 的虛擬函數 MeasureItem() 和 DrawItem()。當 CMenuEx 繼承 CMenu 並覆寫這兩個函數時,基類指標指向派生類物件,多型機制自然觸發自繪邏輯——這才是 C++ 虛擬函數的威力所在。

參考 WINCORE.CPP 中 CWnd::OnMeasureItem 的原始碼可見:框架先取得主選單指標(CMenu*),再透過 _AfxFindPopupMenuFromID 定位目標彈出選單,最後呼叫 pMenu->MeasureItem()。同理,DrawItem 亦同樣流程。

3. 子菜單高度計算的 Hack 與缺陷

原程式將分隔符的 Command ID 從 0 改為 -1,企圖欺騙 MFC 呼叫 MeasureItem() 計算子菜單高度。但 ID 為 -1 的項目永遠不會被 OnMeasureItem 找到,導致無分隔符時子菜單高度不可調。這是促使重寫類別的主因之一。

完美解決方案:CMenuEx 重構要點

  1. 正確覆寫虛擬函數:在 CMenuEx 中實作 MeasureItem 與 DrawItem,完全依賴 MFC 多型機制,無需手動映射訊息。
  2. 處理動態菜單:在主框架類別覆寫 OnInitMenuPopup,對系統自動生成的子菜單(如 MRU)進行子類別化或重新繪製。
  3. 統一項目度量邏輯:為所有類型項目(含子菜單、分隔符)提供一致的度量計算,移除 ID 偽造手法。
  4. 支援真彩色與圖示:在 DrawItem 中使用 CDC::DrawState 或 GDI+ 繪製 32 位圖示、漸變背景、懸停特效等。

關鍵程式碼片段

// CMenuEx.h
class CMenuEx : public CMenu {
public:
    virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMIS);
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
    // 輔助:計算項目高度、寬度
    int CalcItemHeight(UINT nID);
    int CalcItemWidth(UINT nID, CDC* pDC);
};

// CMenuEx.cpp
void CMenuEx::MeasureItem(LPMEASUREITEMSTRUCT lpMIS) {
    // 統一計算高度:文字、圖示、邊距
    lpMIS->itemHeight = CalcItemHeight(lpMIS->itemID);
    lpMIS->itemWidth  = CalcItemWidth(lpMIS->itemID, CDC::FromHandle(lpMIS->hDC));
}

void CMenuEx::DrawItem(LPDRAWITEMSTRUCT lpDIS) {
    CDC dc; dc.Attach(lpDIS->hDC);
    CRect rc(lpDIS->rcItem);
    // 繪製背景、圖示、文字、快捷鍵、子菜單箭頭
    // 根據 lpDIS->itemState 处理选中、禁用、检查状态
    dc.Detach();
}

實戰效果對比

資源編輯器中的菜單樣式

映射訊息後的正確執行效果

未映射訊息的錯誤執行效果

結語

真正的自繪菜單不應依賴 Hack,而應建立在對 MFC 訊息路由與虛擬函數機制的深刻理解之上。透過正確繼承 CMenu 並覆寫核心虛擬函數,配合主框架對動態菜單的攔截,即可實現穩定、高效、真彩色的專業級菜單系統。

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