Vous ne pouvez pas modifier l’état d’un élément de menu à partir de son gestionnaire d’interface utilisateur de commande si le menu est associé à une boîte de dialogue dans Visual C++


Remarque Microsoft Visual C++ .NET 2002 et Visual C++ .NET 2003 prise en charge à la fois le code managé de modèle qui est fourni par le Microsoft.NET Framework et le modèle de code Microsoft Windows natif non managé. Les informations contenues dans cet article s’applique uniquement au code Visual C++ non managé. Microsoft Visual C++ 2005 prend en charge le modèle de code managé qui est fourni par le Microsoft.NET Framework et le modèle de code Microsoft Windows natif non managé.

Symptômes


Modification de l’état (activation/désactivation, activation/désactivation, modifier le texte) d’un élément de menu à partir de son gestionnaire d’interface utilisateur (IU) de commande ne fonctionne pas correctement si le menu est associé à une boîte de dialogue :
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.}

Cause


Lorsqu’un menu déroulant s’affiche, le message WM_INITMENUPOPUP est envoyé avant l’affichage des éléments de menu. La fonction MFC CFrameWnd::OnInitMenuPopup parcourt les éléments de menu et appelle le Gestionnaire de l’interface utilisateur de commande de mise à jour pour l’article, s’il en existe un. L’apparence de chaque élément de menu est mis à jour pour refléter l’état (activé/désactivé, activé/désactivé). Le mécanisme de l’interface utilisateur de mise à jour ne fonctionne pas pour une application basée sur une boîte de dialogue parce que CDialog ne possède aucun gestionnaire de OnInitMenuPopup et il utilise le gestionnaire par défaut de CWndde, qui n’appelle pas de gestionnaires d’interface utilisateur de commande de mise à jour des éléments de menu.

Résolution


Pour résoudre ce problème, utilisez les étapes suivantes :
  1. Ajoutez une entrée ON_WM_INITMENUPOPUP à la table des messages :
    BEGIN_MESSAGE_MAP(CTestDlg, CDialog)//}}AFX_MSG_MAPON_WM_INITMENUPOPUP()END_MESSAGE_MAP()
  2. Ajouter une fonction de membre OnInitMenuPopup pour votre classe de boîte de dialogue et copiez le code suivant (Notez que ce code est extraite en grande partie CFrameWnd::OnInitMenuPopup dans WinFrm.cpp) :
    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;    }}

Statut


Ce comportement est inhérent au produit.

Informations supplémentaires


Le Gestionnaire de l’interface utilisateur de commande de mise à jour est également appelé depuis la fonction membre CWnd::OnCommand pour vous assurer que la commande n’a pas devenir désactivé avant le routage. C’est pourquoi le Gestionnaire de commandes n'est pas appelé pour un élément de menu désactivé même s’il n’est pas grisé (non disponible). Éléments de menu ne sont pas dessinés pour refléter leur statut dans ce cas. C’est le code lié à partir du fichier 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;   }

Procédure pour reproduire le problème

Suivez ces étapes pour reproduire ce comportement dans Visual C++ .NET :
  1. Créer une application de boîte de dialogue MFC à l’aide de AppWizard.
  2. Créer une nouvelle ressource de menu et ajouter les éléments de menu Fichier/quitter et de fichier pour elle.
  3. Définir ce menu comme le menu de la boîte de dialogue dans la fenêtre de propriétés de boîte de dialogue. Pour ce faire, ouvrez la ressource de la boîte de dialogue dans l’éditeur de la boîte de dialogue. Dans la fenêtre Propriétés, cliquez sur Sélectionner le Menu. L’ID de la nouvelle ressource de menu est affiché dans la liste déroulante de Menu propriété éditeur.
  4. Ajoutez un gestionnaire UPDATE_COMMAND_UI pour l’élément de menu Fichier/quitter . Pour cela, en cliquant sur Fichier/quitter dans l’éditeur de menus, puis cliquez sur Ajouter un gestionnaire d’événements. Dans l’Assistant Gestionnaire d’événements, ajoutez le gestionnaire UPDATE_COMMAND_UI au projet les classes dérivées de CDialog . Cliquez sur Ajouter et modifier pour créer le gestionnaire et ajoutez une de ces instructions à la méthode de gestionnaire généré :
    pCmdUI->Enable(FALSE); //Not calling the handler, but does not show as disabledpCmdUI->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. Générez et exécutez l’application.

Références


Pour plus d’informations, cliquez sur le numéro ci-dessous pour afficher l’article correspondant dans la Base de connaissances Microsoft :
Barres de contrôles 141751 ajout aux boîtes de dialogue MFC