您無法從其 command 使用者介面處理常式變更功能表項目的狀態(如果功能表附加至 Visual c + + 中的對話方塊)

本文可協助您解決當功能表附加至 Visual c + + 中的對話方塊時所發生的問題。

原始產品版本:   Visual c + +、.NET 2002
原始 KB 編號:   242577

徵狀

注意

Microsoft Visual c + + .NET 2002 和 Visual c + + .NET 2003 都支援 .NET Framework 和非受管理的原生 Windows 程式碼模型所提供的 managed 程式碼模型。 本文中的資訊僅適用于非受管理的 Visual c + + 程式碼。 Visual c + + 2005 同時支援 .NET Framework 和非受管理的原生 Windows 程式碼模型所提供的 managed 程式碼模型。

變更狀態 (啟用/停用、檢查/取消核取功能表項目目的文字) 從其命令使用者介面 (UI) 處理常式無法在功能表附加至對話方塊時正確運作:

void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI)
{
    pCmdUI->Enable(FALSE); //Not calling the command handler, but does not show as disabled.
    pCmdUI->SetCheck(TRUE); // Does not show check mark before the text.
    pCmdUI->SetRadio(TRUE); // Does not show dot before the text.
    pCmdUI->SetText("Close"); //Does not change the text.
}

原因

當顯示下拉式功能表時, WM_INITMENUPOPUP 會先傳送郵件,再顯示功能表項目。 MFC CFrameWnd::OnInitMenuPopup 函數會逐一查看功能表項目,並呼叫專案的更新命令 UI 處理常式(如果有的話)。 每個功能表項目的外觀都會更新,以反映其狀態 (啟用/停用、已檢查/已檢查/未檢查) 。

由於 CDialog 沒有 OnInitMenuPopup 處理常式,而且它使用 CWnd 的預設處理常式(不會呼叫功能表項目的更新命令 UI 處理常式),所以更新 UI 機制不適用於以對話方塊為基礎的應用程式。

解決方案

請使用下列步驟來解決此問題:

  1. ON_WM_INITMENUPOPUP 專案新增至郵件對應:

    BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
    // }} AFX_MSG_MAP
    
    ON_WM_INITMENUPOPUP()
    END_MESSAGE_MAP()
    
  2. OnInitMenuPopup member 函數新增至對話方塊類別,並複製下列程式碼:

    注意

    這段程式碼基本上從 CFrameWnd: OnInitMenuPopup in WinFrm)

void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu)
{
    ASSERT(pPopupMenu != NULL);
    // Check the enabled state of various menu items.

    CCmdUI state;
    state.m_pMenu = pPopupMenu;
    ASSERT(state.m_pOther == NULL);
    ASSERT(state.m_pParentMenu == NULL);

    // Determine if menu is popup in top-level menu and set m_pOther to
    // it if so (m_pParentMenu == NULL indicates that it is secondary popup).
    HMENU hParentMenu;
    if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
    state.m_pParentMenu = pPopupMenu; // Parent == child for tracking popup.
    else if ((hParentMenu = ::GetMenu(m_hWnd))!= NULL)
    {
        CWnd* pParent = this;
        // Child windows don't have menus--need to go to the top!
        if (pParent != NULL &&
        (hParentMenu = ::GetMenu(pParent->m_hWnd))!= NULL)
        {
            int nIndexMax = ::GetMenuItemCount(hParentMenu);
            for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
            {
                if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)
            {
                // When popup is found, m_pParentMenu is containing menu.
                state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
                break;
            }
            }
        }
    }

    state.m_nIndexMax = pPopupMenu->GetMenuItemCount();
    for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
    state.m_nIndex++)
    {
        state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);
        if (state.m_nID == 0)
        continue; // Menu separator or invalid cmd - ignore it.

        ASSERT(state.m_pOther == NULL);
        ASSERT(state.m_pMenu != NULL);
        if (state.m_nID == (UINT)-1)
        {
            // Possibly a popup menu, route to first item of that popup.
            state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);
            if (state.m_pSubMenu == NULL ||
            (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
            state.m_nID == (UINT)-1)
            {
            continue; // First item of popup can't be routed to.
            }
            state.DoUpdate(this, TRUE); // Popups are never auto disabled.
        }
        else
        {
            // Normal menu item.
            // Auto enable/disable if frame window has m_bAutoMenuEnable
            // set and command is _not_ a system command.
            state.m_pSubMenu = NULL;
            state.DoUpdate(this, FALSE);
        }

        // Adjust for menu deletions and additions.
        UINT nCount = pPopupMenu->GetMenuItemCount();
        if (nCount < state.m_nIndexMax)
        {
            state.m_nIndex -= (state.m_nIndexMax - nCount);
            while (state.m_nIndex < nCount &&
            pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
            {
                state.m_nIndex++;
            }
        }
        state.m_nIndexMax = nCount;
    }
}

狀態

產生此錯誤是系統刻意為之。

其他資訊

更新命令 UI 處理常式也稱為 CWnd::OnCommand ,以確保命令在路由傳送之前未停用。 這就是為何無法在已停用的功能表項目目上呼叫命令處理常式,即使它沒有變暗 (無法使用) 。 在此情況下,不會繪製功能表項目以反映其狀態。 這是 Wincore 檔案中的相關程式碼:

    // Make sure command has not become disabled before routing.
CTestCmdUI state;
state.m_nID = nID;
OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, &state, NULL);
if (!state.m_bEnabled)
{
    TRACE1("Warning: not executing disabled command %d\n", nID);
    return TRUE;
}

再現行為的步驟

請遵循下列步驟,在 Visual c + + .NET 中重現此行為:

  1. 使用應用程式建立 MFC 對話方塊型應用程式。

  2. 建立新的功能表資源 ,並將檔案和檔案 /出口 功能表項目目新增至該資源。

  3. 在對話方塊的 [屬性] 視窗中,將此功能表設定為對話方塊的功能表。 若要這麼做,請在對話方塊編輯器中開啟對話方塊資源。 在 [ 屬性 ] 視窗中,按一下 [ 選取功能表 ]。 新功能表資源的識別碼會顯示在功能表屬性編輯器的下拉式清單中。

  4. 新增 UPDATE_COMMAND_UI File/Exit 功能表項目目的處理常式。 若要執行此動作,請在功能表編輯器中以滑鼠右鍵按一下 [檔案 /退出 ],然後按一下 [ 新增事件處理常式 ]。 在事件處理常式嚮導中,將 UPDATE_COMMAND_UI 處理常式新增至專案 CDialog 衍生類別。 按一下 [ 新增 ] 和 [ 編輯 ] 以建立處理常式,然後將其中一個語句新增至所產生的處理常式方法:

    pCmdUI->Enable(FALSE); //Not calling the handler, but does not show as disabled
    pCmdUI->SetCheck(TRUE); // Does not show check mark before the text.
    pCmdUI->SetRadio(TRUE); // Does not show dot before the text.
    pCmdUI->SetText("Close"); //Does not change the text.
    
  5. 建立並執行應用程式。