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

MFC 彈出式選單 (Popup Menu) 完整實作指南:資源建立、CMenu 操作、座標轉換、訊息回應

     編輯:VC 教學

什麼是彈出式選單?

彈出式選單 (Popup Menu/Context Menu) 是無標題列、浮動於游標位置的選單,通常由滑鼠右鍵觸發 (WM_CONTEXTMENU),也可程式控制在任意位置顯示 (WM_LBUTTONDOWN、熱鍵、長按等)。MFC 透過 CMenu 類別封裝 Win32 選單 API。

步驟 1:資源編輯器建立選單資源

  1. 資源檢視 → 右鍵 Menu → Insert Menu。
  2. ID 設為 IDR_POPUP_MENU (或 IDC_POPMENU)。
  3. 建立一個主選單項 (如「操作」),其下建立實際命令項 (複製、貼上、刪除、屬性...)。
  4. 關鍵:MFC 不支援直接建立「無標題的彈出選單資源」,必須建立一個主選單項,其第一層子選單即為我們要的彈出選單內容。主選單項標題不會顯示,僅作為容器。

步驟 2:ClassWizard 加入命令處理函式

對每個子選單項 (如 ID_POPUP_COPY、ID_POPUP_DELETE) 加入 COMMAND 訊息處理函式,邏輯與主選單項相同。

步驟 3:回應 WM_CONTEXTMENU (滑鼠右鍵) 彈出選單


// View 或 Dialog 類別
void CMyView::OnContextMenu(CWnd* pWnd, CPoint point)
{
    // point 為螢幕座標
    CMenu menu;
    if (!menu.LoadMenu(IDR_POPUP_MENU)) return;

    // 取得第一層子選單 (索引 0)
    CMenu* pPopup = menu.GetSubMenu(0);
    if (!pPopup) return;

    // 彈出選單:左對齊、游標位置、擁有者視窗
    pPopup->TrackPopupMenu(
        TPM_LEFTALIGN | TPM_RIGHTBUTTON, // 右鍵選單標準旗標
        point.x, point.y,
        this, // 擁有者視窗,接收 COMMAND 訊息
        NULL  // 不限制選單區域
    );
    // 選單銷毀時自動釋放 CMenu 物件 (附屬於 menu 區域變數)
}

TrackPopupMenu 旗標詳解

旗標用途
TPM_LEFTALIGN選單左緣對齊 x 座標 (標準)
TPM_CENTERALIGN選單水平置中於 x 座標
TPM_RIGHTALIGN選單右緣對齊 x 座標
TPM_RIGHTBUTTON允許右鍵選取項目 (配合 WM_CONTEXTMENU 必加)
TPM_LEFTBUTTON僅左鍵選取 (預設)
TPM_NONOTIFY不發送 WM_COMMAND,改由回傳值取得選中項目 ID
TPM_RETURNCMD配合 TPM_NONOTIFY,直接回傳選中命令 ID

步驟 4:在其他情境彈出選單 (如左鍵點擊、熱鍵)


void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // point 為用戶區座標,必須轉為螢幕座標
    ClientToScreen(&point);

    CMenu menu;
    menu.LoadMenu(IDR_POPUP_MENU);
    CMenu* pPopup = menu.GetSubMenu(0);
    if (pPopup)
    {
        pPopup->TrackPopupMenu(
            TPM_LEFTALIGN, // 左鍵觸發不需 TPM_RIGHTBUTTON
            point.x, point.y,
            this, NULL);
    }

    // 還原 point 為用戶區座標 (若後續基類處理需要)
    ScreenToClient(&point);
    CView::OnLButtonDown(nFlags, point);
}

進階技巧:動態修改選單項狀態 (啟用/禁用、打勾、文字)

在彈出前即時調整:


void CMyView::OnContextMenu(CWnd* pWnd, CPoint point)
{
    CMenu menu;
    menu.LoadMenu(IDR_POPUP_MENU);
    CMenu* pPopup = menu.GetSubMenu(0);
    if (!pPopup) return;

    // 根據應用狀態動態更新
    pPopup->EnableMenuItem(ID_POPUP_PASTE, 
        m_bClipboardHasData ? MF_ENABLED : MF_GRAYED); // 由狀態決定啟用/灰顯
    pPopup->CheckMenuItem(ID_POPUP_VIEW_GRID, 
        m_bShowGrid ? MF_CHECKED : MF_UNCHECKED); // 打勾狀態
    pPopup->ModifyMenu(ID_POPUP_RENAME, MF_BYCOMMAND, ID_POPUP_RENAME, 
        m_strSelectedFile.IsEmpty() ? _T("重新命名") : _T("重新命名: ") + m_strSelectedFile);

    pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this, NULL);
}

進階技巧:鍵盤快速鍵與無障礙支援

  • 資源編輯器中為選單項設定「Prompt」欄位 (狀態列提示文字)。
  • 在選單項標題加入 & 字元指定快速鍵 (如 &Copy\tCtrl+C),\t 後為顯示快捷鍵提示。
  • 確保擁有者視窗 (this) 具有焦點或呼叫 SetForegroundWindow(),否則鍵盤導航可能失效。

常見問題排查

現象原因解決
選單不顯示LoadMenu 失敗、GetSubMenu 索引錯誤、point 座標錯誤檢查資源 ID、確認子選單索引 (通常為 0)、ClientToScreen 轉換
選單閃爍即消失未設 TPM_RIGHTBUTTON (右鍵觸發時)、擁有者視窗非前景加上 TPM_RIGHTBUTTON、TrackPopupMenu 前呼叫 SetForegroundWindow()
命令不觸發訊息映射缺失、命令 ID 錯誤、擁有者視窗不接收訊息ClassWizard 加入 ON_COMMAND、確認 ID 一致、擁有者為 CWnd 派生類
圖示不顯示MFC 預設不支援選單項圖示使用 Owner-draw (OD) 選單或第三方庫 (BCGControlBar, Ultimate Toolbox)

現代替代方案:CMFCPopupMenu (MFC Feature Pack / VS2008+)

支援 Office 風格、圖示、分隔線、動畫、鍵盤搜尋、觸控友善:


#include 

void CMyView::OnContextMenu(CWnd* pWnd, CPoint point)
{
    CMFCPopupMenu* pMenu = new CMFCPopupMenu;
    pMenu->Create(this, point.x, point.y, IDR_POPUP_MENU); // 直接用資源 ID
    // 自動管理生命週期,點擊外部自動銷毀
}

結語

彈出式選單是桌面應用交互的核心元件。掌握 CMenu::LoadMenu、GetSubMenu、TrackPopupMenu 三步驟核心流程,配合座標轉換 (ClientToScreen) 與動態項目狀態更新,即可實現專業級右鍵選單。現代專案建議直接使用 CMFCPopupMenu 獲得更豐富視覺與交互體驗。

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