傳統 MFC 自繪選單通常需派生 CMenu 子類別並處理 WM_DRAWITEM/WM_MEASUREITEM,過程繁瑣。本文展示僅在 CMainFrame 中新增三個訊息處理函數,即可為框架選單套用自繪風格,程式碼簡潔、易維護,適合快速專案整合。
OnMeasureItem):設定選單項目高寬,區分一般項目與分隔線。OnDrawItem):繪製選單項目背景、選取態、停用態文字顏色、左側圖示區與文字輸出。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。OnInitMenu 控制,需改用 OnInitMenuPopup 處理(參考進階文章《完美實現真彩自繪選單》)。此法適合快速為現有專案換膚,若需支援圖示、真彩、動態選單等進階功能,建議改用完整的 CMenuEx 架構。