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

自繪選單實作:CCustomMenu 類別支援圖示、顏色、高亮風格完整定製

     編輯:關於C語言

自繪選單核心機制

Windows 選單項目設定 MF_OWNERDRAW 旗標後,系統不再繪製預設外觀,改由擁有者視窗處理 WM_MEASUREITEM (計算尺寸) 和 WM_DRAWITEM (繪製內容) 訊息。MFC 將此封裝為 CWnd::OnMeasureItem/OnDrawItem 虛擬函式。

CCustomMenu 類別設計


class CCustomMenu : public CMenu {
public:
    struct MENUDATA {
        CString menuText;       // 顯示文字
        UINT menuIconNormal;    // 正常狀態圖示資源 ID
        UINT menuIconSelected;  // 選中狀態圖示資源 ID (可選)
    };

    // 樣式設定
    void SetIconSize(int cx, int cy);           // 圖示尺寸 (一次設定,改變需重建選單)
    void SetHighlightStyle(UINT nStyle);        // Normal / TextOnly / ...
    void SetTextColor(COLORREF clr);
    void SetBackColor(COLORREF clr);
    void SetHighlightColor(COLORREF clr);

    // 新增項目 (覆寫 AppendMenu/InsertMenu 接受 MENUDATA*)
    BOOL AppendMenu(UINT nFlags, UINT nIDNewItem, const MENUDATA* pData);
    BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT nIDNewItem, const MENUDATA* pData);

protected:
    // 內部繪製邏輯
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
    virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMIS);
};

使用範例


// 1. 定義成員
CCustomMenu m_customMenu;
CCustomMenu::MENUDATA menuData[3];

// 2. 初始化 (如 OnCreate 或 OnInitMenuPopup)
m_customMenu.CreateMenu();
m_customMenu.SetIconSize(16, 16);
m_customMenu.SetHighlightStyle(CCustomMenu::Normal); // 或 TextOnly
m_customMenu.SetTextColor(RGB(0, 0, 0));
m_customMenu.SetBackColor(RGB(255, 255, 255));
m_customMenu.SetHighlightColor(RGB(0, 0, 255));

// 3. 填充資料並新增項目
menuData[0].menuText = _T("開啟檔案(&O)");
menuData[0].menuIconNormal = IDI_ICON_OPEN;
m_customMenu.AppendMenu(MF_OWNERDRAW, ID_FILE_OPEN, &menuData[0]);

menuData[1].menuText = _T("儲存(&S)");
menuData[1].menuIconNormal = IDI_ICON_SAVE;
m_customMenu.AppendMenu(MF_OWNERDRAW, ID_FILE_SAVE, &menuData[1]);

// 4. 指派給視窗
SetMenu(&m_customMenu);

視窗類別必須處理 WM_MEASUREITEM


void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMIS) {
    if (lpMIS->CtlType == ODT_MENU && m_customMenu.IsMenu((HMENU)lpMIS->itemID)) {
        m_customMenu.MeasureItem(lpMIS); // 委託給 CCustomMenu
    } else {
        CView::OnMeasureItem(nIDCtl, lpMIS);
    }
}

void CMyView::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDIS) {
    if (lpDIS->CtlType == ODT_MENU && m_customMenu.IsMenu((HMENU)lpDIS->itemID)) {
        m_customMenu.DrawItem(lpDIS);
    } else {
        CView::OnDrawItem(nIDCtl, lpDIS);
    }
}

DrawItem 關鍵繪製邏輯

  1. 取得 MENUDATA* 從 lpDIS->itemData。
  2. 根據 lpDIS->itemState (ODS_SELECTED、ODS_GRAYED、ODS_DISABLED、ODS_CHECKED) 決定顏色、筆刷、圖示。
  3. 填充背景:FillRect 使用選中/正常背景色。
  4. 繪製圖示:DrawIconEx 或 DrawState (支援灰階停用態)。
  5. 繪製文字:DrawText 計算文字區域 (扣除圖示寬度),支援 & 加速鍵底線。
  6. 繪製檢查標記區域 (保留左側空間)。

Marlett 字型繪製箭頭/標準符號

Windows 系統字型 Marlett 內建標準 UI 符號:

  • 0x30 (字元 '0') → 最大化方塊
  • 0x31 ('1') → 最小化線
  • 0x32 ('2') → 還原重疊方塊
  • 0x33 ('3') → 關閉叉叉
  • 0x34 ('4') → 向下箭頭 (下拉選單)
  • 0x35 ('5') → 向右箭頭 (子選單)

使用方式:CFont font; font.CreateFont(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, _T("Marlett")); 選入 DC 後 TextOut 對應字元即可畫出完美縮放的系統符號。

資源管理注意

  • CCustomMenu 解構時自動 DestroyMenu,若手動 Detach 需自行管理。
  • 圖示資源 (IDI_ICON_*) 需在專案資源中定義,大小匹配 SetIconSize。
  • 高 DPI 下建議使用向量圖示或多尺寸圖示資源,LoadImage 配合 LR_DEFAULTSIZE。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved