傳統動態選單實現需手動呼叫 GetMenu() → CMenu::AppendMenu/ModifyMenu/DeleteMenu,代碼分散且易出錯。MFC 的 訊息映射 機制配合 ON_UPDATE_COMMAND_UI 宏與 CCmdUI 類別,提供了宣告式、自動化的選單狀態更新方案:啟用/停用、文字修改、打勾/單選標記、彈出選單啟用狀態同步,全部在 UI 即將顯示前自動觸發。
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); // 更新處理函數原型
| 方法 | 功能 |
|---|---|
Enable(BOOL bOn) | 啟用/停用 (灰化) 選單項 |
SetText(LPCTSTR) | 動態設定顯示文字 |
SetCheck(int nCheck) | 設定打勾標記 (0=無, 1=打勾, 2=單選點) |
SetRadio(BOOL bOn) | 設定單選圓點標記 |
彈出選單本身無 ID,無法直接映射。MFC 規定:彈出選單的第一個項目更新函數會被呼叫兩次:
pCmdUI->m_pSubMenu == NULL。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);
}
pCmdUI->m_pMenu->DeleteMenu/InsertMenu,但需謹慎管理索引。ON_UPDATE_COMMAND_UI,自動同步啟用/按下狀態。CCmdUI::m_pOther (指向觸發視圖/框架) 實現視圖相關的選單狀態。ON_UPDATE_COMMAND_UI + CCmdUI 是 MFC 動態選單的「正規軍」做法:將狀態邏輯集中在文檔/視圖類別,由框架自動在恰當時機驅動 UI 更新,消除了手動 GetMenu() 操作的時序與耦合問題。掌握 m_pSubMenu 雙重呼叫機制,是處理巢狀彈出選單的關鍵。