Adding UpdateCommandUI to Dialog Menus

Published on April 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The UpdateCommandUI functionality is great for disabling menu items when data is set to a certain value. Adding this functionality to a dialog based application is a little tricky, but it can be done.

We first need to add the OnInitMenuPopup() member function to our dialog box class. Here is the code that should get generated by default for this event:

void CTestDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
    CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}

Adding the Necessary Code

We need to change this code, so delete (or comment out) the single line of code in this function, replacing it with the following:

ASSERT(pPopupMenu != NULL);
CCmdUI state; // Check the enabled state of various menu items
state.m_pMenu = pPopupMenu;
ASSERT(state.m_pOther == NULL);
ASSERT(state.m_pParentMenu == NULL);

// Is the menu in question a popup in the top-level menu? If so,
// set m_pOther to this menu. Note that m_pParentMenu == NULL
// indicates that the menu is a secondary popup.

HMENU hParentMenu;
if(AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
    // Parent == child for tracking popup.
    state.m_pParentMenu = pPopupMenu;
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.
        }
        // Popups are never auto disabled.
        state.DoUpdate(this, TRUE);
    }
    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;
}

The code presented above comes from the MFC Documentation, and is adapted from CFrameWnd::OnInitMenuPopup() in WinFrm.cpp. With this code in place, you can now call the UpdateCommandUI interface for an embedded menu control. A handy addition to any dialog box that contains a menu.

Comments are closed.

Copyright © 2004-2018 Jonah Bishop. Hosted by DreamHost.