Sie können den Status eines Menüelements nicht über den Befehlsbenutzeroberflächenhandler ändern, wenn das Menü an ein Dialogfeld in Visual C++ angefügt ist.

Dieser Artikel hilft Ihnen bei der Behebung des Problems, das auftritt, wenn das Menü an ein Dialogfeld in Visual C++ angefügt ist.

Originalversion des Produkts:   Visual C++, .NET 2002
Ursprüngliche KB-Nummer:   242577

Problembeschreibung

Hinweis

Microsoft Visual C++ .NET 2002 und Visual C++ .NET 2003 unterstützen sowohl das verwaltete Codemodell, das vom .NET Framework bereitgestellt wird, als auch das nicht verwaltete systemeigene Windows Codemodell. Die Informationen in diesem Artikel gelten nur für nicht verwalteten Visual C++-Code. Visual C++ 2005 unterstützt sowohl das verwaltete Codemodell, das vom .NET Framework bereitgestellt wird, als auch das nicht verwaltete systemeigene Windows Codemodell.

Das Ändern des Status (Aktivieren/Deaktivieren, Aktivieren/Deaktivieren, Ändern von Text) eines Menüelements über den Benutzeroberflächenhandler des Befehls funktioniert nicht ordnungsgemäß, wenn das Menü an ein Dialogfeld angefügt ist:

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.
}

Ursache

Wenn ein Dropdownmenü angezeigt wird, wird die WM_INITMENUPOPUP Nachricht gesendet, bevor die Menüelemente angezeigt werden. Die MFC-Funktion CFrameWnd::OnInitMenuPopup durchläuft die Menüelemente und ruft den Updatebefehl-UI-Handler für das Element auf, sofern vorhanden. Die Darstellung jedes Menüelements wird entsprechend seinem Status aktualisiert (aktiviert/deaktiviert, aktiviert/deaktiviert).

Der Update-UI-Mechanismus funktioniert nicht für eine dialogfeldbasierte Anwendung, da CDialog kein OnInitMenuPopup Handler vorhanden ist und der Standardhandler von CWnd verwendet wird, der keine Updatebefehls-UI-Handler für Menüelemente aufruft.

Lösung

Führen Sie die folgenden Schritte aus, um dieses Problem zu beheben:

  1. Hinzufügen eines ON_WM_INITMENUPOPUP Eintrags zur Nachrichtenzuordnung:

    BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
    // }} AFX_MSG_MAP
    
    ON_WM_INITMENUPOPUP()
    END_MESSAGE_MAP()
    
  2. Fügen Sie ihrer Dialogfeldklasse eine OnInitMenuPopup Memberfunktion hinzu, und kopieren Sie den folgenden Code:

    Hinweis

    Dieser Code stammt größtenteils aus CFrameWnd::OnInitMenuPopup in WinFrm.cpp)

void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu)
{
    // Make sure this is actually a menu. When clicking the program icon
    // in the window title bar this function will trigger and pPopupMenu 
    // will NOT be a menu.
    if (!IsMenu(pPopupMenu->m_hMenu))
        return;
        
    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;
    }
}

Status

Es handelt sich hierbei um ein beabsichtigtes Verhalten.

Weitere Informationen

Der Benutzeroberflächenhandler für aktualisierungsbefehle wird auch aufgerufen CWnd::OnCommand , um sicherzustellen, dass der Befehl vor dem Routing nicht deaktiviert wurde. Aus diesem Grund wird der Befehlshandler nicht für ein deaktiviertes Menüelement aufgerufen, obwohl es nicht abgeblendet (nicht verfügbar) ist. Menüelemente werden in diesem Fall nicht gezeichnet, um ihren Status widerzuspiegeln. Dies ist der zugehörige Code aus der Datei "Wincore.cpp":

    // 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;
}

Schritte zum Reproduzieren des Verhaltens

Führen Sie die folgenden Schritte aus, um dieses Verhalten in Visual C++ .NET zu reproduzieren:

  1. Erstellen Sie eine dialogbasierte MFC-Anwendung mithilfe von AppWizard.

  2. Erstellen Sie eine neue Menüressource, und fügen Sie ihr die Menüelemente "Datei " und " Datei/ Beenden " hinzu.

  3. Legen Sie dieses Menü als Menü für das Dialogfeld im Dialogfeld Eigenschaftenfenster fest. Öffnen Sie dazu die Dialogressource im Dialog-Editor. Klicken Sie im Eigenschaftenfenster auf "Menü auswählen". Die ID der neuen Menüressource wird in der Dropdownliste des Menüeigenschaften-Editors angezeigt.

  4. Fügen Sie einen UPDATE_COMMAND_UI Handler für das Menüelement "Datei/Beenden " hinzu. Klicken Sie dazu im Menü-Editor mit der rechten Maustaste auf "Datei/Beenden ", und klicken Sie dann auf " Ereignishandler hinzufügen". Fügen Sie im Ereignishandler-Assistenten den UPDATE_COMMAND_UI Handler der vom Projekt CDialog abgeleiteten Klasse hinzu. Klicken Sie auf "Hinzufügen" und " Bearbeiten ", um den Handler zu erstellen, und fügen Sie dann eine der folgenden Anweisungen zur generierten Handlermethode hinzu:

    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. Erstellen Sie die Anwendung, und führen Sie sie aus.