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

MFC 訊息映射機制實現動態選單:ON_UPDATE_COMMAND_UI 與 CCmdUI 完全指南

     編輯:關於VC++

前言

傳統動態選單實現需手動呼叫 GetMenu() → CMenu::AppendMenu/ModifyMenu/DeleteMenu,代碼分散且易出錯。MFC 的 訊息映射 機制配合 ON_UPDATE_COMMAND_UI 宏與 CCmdUI 類別,提供了宣告式、自動化的選單狀態更新方案:啟用/停用、文字修改、打勾/單選標記、彈出選單啟用狀態同步,全部在 UI 即將顯示前自動觸發。

一、MFC 訊息映射速覽

class CMyClass : public CBaseClass {
    DECLARE_MESSAGE_MAP()
    // ...
};

BEGIN_MESSAGE_MAP(CMyClass, CBaseClass)
    ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
    ON_UPDATE_COMMAND_UI(ID_MENU_ITEM, OnUpdateMenuItem)
END_MESSAGE_MAP()

afx_msg void OnUpdateMenuItem(CCmdUI* pCmdUI); // 更新處理函數原型

二、CCmdUI 核心方法

方法功能
Enable(BOOL bOn)啟用/停用 (灰化) 選單項
SetText(LPCTSTR)動態設定顯示文字
SetCheck(int nCheck)設定打勾標記 (0=無, 1=打勾, 2=單選點)
SetRadio(BOOL bOn)設定單選圓點標記

三、ON_UPDATE_COMMAND_UI 觸發時機

  • 使用者點選包含該項目的選單列(或按 Alt 熱鍵)。
  • 使用者按下該項目對應的快捷鍵。
  • 使用者按下同層級任一項目的快捷鍵(整組同步更新)。

四、彈出選單 (Popup) 的特殊處理:m_pSubMenu 區分

彈出選單本身無 ID,無法直接映射。MFC 規定:彈出選單的第一個項目更新函數會被呼叫兩次:

  1. 繪製彈出選單內部項目時:pCmdUI->m_pSubMenu == NULL。
  2. 繪製彈出選單標題本身時:pCmdUI->m_pSubMenu != NULL(指向該彈出選單的 CMenu*)。

利用此特性可同步控制彈出選單整體啟用/停用。

五、完整實戰範例

選單結構

測試選單
├─ 項目一 (ID_ITEM_ONE)  Ctrl+1
├─ 項目二 (ID_ITEM_TWO)  Ctrl+2
└─ 彈出選單 (Popup)
   ├─ 彈出項一 (ID_POPUP_ONE)  Ctrl+3
   └─ 彈出項二 (ID_POPUP_TWO)  Ctrl+4

類別宣告

class CMyDoc : public CDocument {
    BOOL m_bItemOne, m_bItemTwo, m_bPopupOne, m_bPopupTwo; // 狀態旗標
protected:
    afx_msg void OnUpdateItemOne(CCmdUI* pCmdUI);
    afx_msg void OnUpdateItemTwo(CCmdUI* pCmdUI);
    afx_msg void OnUpdatePopupOne(CCmdUI* pCmdUI);
    afx_msg void OnUpdatePopupTwo(CCmdUI* pCmdUI);
    DECLARE_MESSAGE_MAP()
};

訊息映射

BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
    ON_UPDATE_COMMAND_UI(ID_ITEM_ONE, OnUpdateItemOne)
    ON_UPDATE_COMMAND_UI(ID_ITEM_TWO, OnUpdateItemTwo)
    ON_UPDATE_COMMAND_UI(ID_POPUP_ONE, OnUpdatePopupOne)
    ON_UPDATE_COMMAND_UI(ID_POPUP_TWO, OnUpdatePopupTwo)
END_MESSAGE_MAP()

更新處理函數實現

void CMyDoc::OnUpdateItemOne(CCmdUI* pCmdUI)
{
    pCmdUI->Enable(m_bItemOne);
    pCmdUI->SetText(m_bItemOne ? _T("項目一") : _T("項目一 (已停用)"));
}

void CMyDoc::OnUpdateItemTwo(CCmdUI* pCmdUI)
{
    pCmdUI->Enable(m_bItemTwo);
    pCmdUI->SetText(m_bItemTwo ? _T("項目二") : _T("項目二 (已停用)"));
}

void CMyDoc::OnUpdatePopupOne(CCmdUI* pCmdUI)
{
    if (pCmdUI->m_pSubMenu != NULL) { // 正在繪製彈出選單標題 "Popup"
        BOOL bEnable = m_bPopupOne || m_bPopupTwo; // 任一子項啟用即啟用彈出選單
        pCmdUI->m_pMenu->EnableMenuItem(pCmdUI->m_nIndex,
            MF_BYPOSITION | (bEnable ? MF_ENABLED : (MF_DISABLED | MF_GRAYED)));
        return;
    }
    // 繪製子項 "彈出項一"
    pCmdUI->Enable(m_bPopupOne);
}

void CMyDoc::OnUpdatePopupTwo(CCmdUI* pCmdUI)
{
    pCmdUI->Enable(m_bPopupTwo);
}

六、進階技巧

  1. 動態增減項目: 在更新函數中呼叫 pCmdUI->m_pMenu->DeleteMenu/InsertMenu,但需謹慎管理索引。
  2. 工具列按鈕同步: 同一 ID 的工具列按鈕會共用 ON_UPDATE_COMMAND_UI,自動同步啟用/按下狀態。
  3. 效能優化: 更新函數應極簡(僅檢查旗標並呼叫 CCmdUI 方法),避免資料庫查詢或複雜運算。
  4. 上下文敏感: 結合 CCmdUI::m_pOther (指向觸發視圖/框架) 實現視圖相關的選單狀態。

結語

ON_UPDATE_COMMAND_UI + CCmdUI 是 MFC 動態選單的「正規軍」做法:將狀態邏輯集中在文檔/視圖類別,由框架自動在恰當時機驅動 UI 更新,消除了手動 GetMenu() 操作的時序與耦合問題。掌握 m_pSubMenu 雙重呼叫機制,是處理巢狀彈出選單的關鍵。

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