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

三個函數輕鬆實現 MFC 框架選單自繪:無需派生新類別

     編輯:關於VC++

傳統 MFC 自繪選單通常需派生 CMenu 子類別並處理 WM_DRAWITEM/WM_MEASUREITEM,過程繁瑣。本文展示僅在 CMainFrame 中新增三個訊息處理函數,即可為框架選單套用自繪風格,程式碼簡潔、易維護,適合快速專案整合。

核心實作:三大訊息處理函數

  1. WM_MEASUREITEM (OnMeasureItem):設定選單項目高寬,區分一般項目與分隔線。
  2. WM_DRAWITEM (OnDrawItem):繪製選單項目背景、選取態、停用態文字顏色、左側圖示區與文字輸出。
  3. WM_INITMENU (OnInitMenu):遍歷主選單所有子選單項目,加上 MF_OWNERDRAW 標誌,觸發自繪機制。

關鍵程式碼片段

void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMIS) {
    if (lpMIS->CtlType == ODT_MENU) {
        lpMIS->itemHeight = (lpMIS->itemID == ID_SEPARATOR) ? 1 : 20;
        lpMIS->itemWidth = 150;
    }
}

void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDIS) {
    if (nIDCtl == 0 && lpDIS->CtlType == ODT_MENU) {
        CDC* pDC = CDC::FromHandle(lpDIS->hDC);
        pDC->SetBkMode(TRANSPARENT);
        CMenu menu; menu.Attach((HMENU)lpDIS->hwndItem);
        CString strText; menu.GetMenuString(lpDIS->itemID, strText, MF_BYCOMMAND);
        CRect rcAll(lpDIS->rcItem), rcLeft(rcAll.left, rcAll.top, 20, rcAll.bottom);
        CBrush brBack(RGB(250,250,250)), brLeft(RGB(230,230,230)), brSel(RGB(148,170,214));

        if (lpDIS->itemID != ID_SEPARATOR) {
            if (lpDIS->itemAction & ODA_SELECT) { // 選取態
                pDC->FillRect(rcAll, &brSel);
                pDC->SetTextColor((lpDIS->itemState & ODS_GRAYED) ? RGB(194,194,194) : RGB(250,250,250));
            } else { // 非選取態
                pDC->FillRect(rcAll, &brBack);
                pDC->FillRect(rcLeft, &brLeft);
                pDC->SetTextColor((lpDIS->itemState & ODS_GRAYED) ? RGB(194,194,194) : RGB(66,110,180));
            }
        } else {
            pDC->FillRect(rcAll, &brLeft); // 分隔線
        }
        pDC->TextOut(rcAll.left + 30, rcAll.top + 5, strText);
        menu.Detach();
    }
}

void CMainFrame::OnInitMenu(CMenu* pMenu) {
    for (UINT i = 0; i < pMenu->GetMenuItemCount(); ++i) {
        CMenu* pSub = pMenu->GetSubMenu(i);
        if (pSub) {
            for (UINT j = 0; j < pSub->GetMenuItemCount(); ++j) {
                UINT nID = pSub->GetMenuItemID(j);
                pSub->ModifyMenu(j, MF_BYPOSITION | MF_OWNERDRAW, nID);
            }
        }
    }
}

進階優化與已知限制

  • 避免重複修改:在 OnInitMenu 加入旗標變數,標記選單已轉為 Owner-draw,避免每次彈出皆重複處理;或在 OnCreate 一次性完成(前提是選單結構固定)。
  • 子選單彈出項目:預設碼未處理子選單的彈出項目(彈出式選單標題),需額外遞迴遍歷加上 MF_OWNERDRAW。
  • MRU (最近檔案) 清單:系統自動動態插入的 MRU 項目不受 OnInitMenu 控制,需改用 OnInitMenuPopup 處理(參考進階文章《完美實現真彩自繪選單》)。

此法適合快速為現有專案換膚,若需支援圖示、真彩、動態選單等進階功能,建議改用完整的 CMenuEx 架構。

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