Montana Boy Needs Help: Enabling Menu Items On-The-Fly

Dear Dr. GUI:

I'm required to add menu items to a menu on the fly in an MFC-based application. I added the items to the menu using AppendMenu and set them as MF_ENABLED, but when I run the application the new menu items are disabled. What can I do?

Christopher Reed
Billings, Montana

Dr. GUI replies:

Well, Christopher, I am partial to Montanans, having been born there. My mother lived in a sod house in Miles City. My father drove taxi in French Town. Odd, you could walk from one end of French Town to the other in less than three minutes! I was born in Great Falls. I was potty trained by the time I was nine months old. My first word was Grizzly. I panned for gold when I was three. I was a smoke jumper for the forest service and was instrumental in the creation of Glacier Park. Then I started grade school. But that is another story!

The reason these items are being disabled is that in the Microsoft Foundation Class Library (MFC) OnInitMenuPopup function, items that do not have a message handler function are disabled. To fix the problem, simply overwrite OnInitMenuPopup in your CMainFrame class and return when nIndex is equal to the menu to which you added items. The following example demonstrates this.

void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
   if (nIndex == CONTENTMENU)   // contents menu
   {
      return;
   }
   CFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);

}

One caveat: If you are using OnUpdateCmdUI to set the text of a menu item, check a menu item, or enable/disable a menu item, you must add special handling for the menus that you are not allowing MFC to handle. In the following example, the application required a bullet on the currently selected menu item, including the added ones.

void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
   if (nIndex == CONTENTMENU)   // contents menu
   {
      UINT nCurrent;

      // First get a pointer to the menu of interest.
      CMenu *pContentMenu;
      pContentMenu = pPopupMenu->GetSubMenu(CONTENTMENU);

      // Now we have to build a CCmdUI class for the menu items.
      CCmdUI state;

      // Set the m_pMenu member variable to the main menu.
      state.m_pMenu = pPopupMenu;

      // Set the m_pParentMenu to the submenu. Sounds funny but trust me.
      state.m_pParentMenu = pContentMenu;

      // The menu ID of the menu item that you want a bullet on.
      state.m_nID = pView->m_lContent;

      // Must be NULL
      state.m_pSubMenu = NULL;
      state.m_pOther = NULL;

      // Set the m_nIndexMax to the number of menu items in the main menu.
      state.m_nIndexMax = pPopupMenu->GetMenuItemCount();

       // This switch figures out which menu index should be marked.
      switch (state.m_nID)
      {
         // Is it one of the four stock menu items...
         case IDM_PRODUCTS:
            nCurrent = 0;
            break;
         case IDM_CD:
            nCurrent = 1;
            break;
         case IDM_NEW:
            nCurrent = 2;
            break;
         case IDM_UPDATED:
            nCurrent = 3;
            break;
         // ...or an appended menu item.
         default:
            nCurrent = state.m_nID - IDM_DEFINESUBSET - SUBSETMENUOFFSET + 
                       NUM_STOCK_MENUITEMS;
            break;
      }
      // Check the current item and uncheck the rest since we only want one 
      // current.
       for (UINT i = 0; i < state.m_nIndexMax; i++)
      {
         // Set the CCmdUI member variable m_nIndex so that SetRadio knows which
         // menu item to work on.
         state.m_nIndex = i;
         if (i == nCurrent)
            state.SetRadio(TRUE);
         else
            state.SetRadio(FALSE);
      }
      return;
   }
   CFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}

Don't forget to remove any OnUpdateCmdUI functions that you have created for this menu, because they won't get called.