How to Use Multiple Menus in MFC App That Uses GetDefaultMenu (145857)



The information in this article applies to:

  • The Microsoft Foundation Classes (MFC), when used with:
    • Microsoft Visual C++ for Windows, 16-bit edition 1.5
    • Microsoft Visual C++ for Windows, 16-bit edition 1.51
    • Microsoft Visual C++ for Windows, 16-bit edition 1.52
    • Microsoft Visual C++, 32-bit Editions 2.0
    • Microsoft Visual C++, 32-bit Editions 2.1
    • Microsoft Visual C++, 32-bit Editions 2.2
    • Microsoft Visual C++, 32-bit Editions 4.0

This article was previously published under Q145857

SUMMARY

In an MFC application, you may occasionally find it useful to switch between menus within the same frame window, such as when using static splitter windows or objects in a view requiring a whole new menu. Because the framework provides automatic support for only one menu per document template (hence per frame), additional code must be added to switch automatically between multiple menu resources. MFC provides the undocumented virtual function CDocument::GetDefaultMenu() to allow the document to determine which menu to display.

MORE INFORMATION

When AppWizard generates an SDI or MDI application, it creates a menu resource by using IDR_MAINFRAME as its resource ID. This menu is displayed at all times for an SDI application and only when there are no active documents in an MDI application. For an MDI application, AppWizard also generates a menu resource for the one multidoc template it creates using IDR_xxxxTYPE as its resource ID. Additional menu resources can be used by creating them using the resource editor and associating them with another multidoc template (see CMultiDocTemplate). The framework will display the menu resource associated with each multidoc template automatically. This method limits you one menu per document template. To bypass this limitation, you can override CDocument::GetDefaultMenu() and perform some calls to display the menu.

MFC uses CMDIChildWnd::m_hMenuShared and CFrameWnd::m_hMenuDefault data members to determine which menu to display. In an MDI application, the menu resource associated with each document template is loaded during the construction of the document templates and copied into CMDIChildWnd::m_hMenuShared. This menu is then used in CMDIChildWnd::OnUpdateFrameMenu() to set the menu of the MDI Frame window when there is an active MDI child window. CMDIChildWnd::OnUpdateFrameMenu() uses CFrameWnd::m_hMenuDefault, which is loaded during CFrameWnd construction using IDR_MAINFRAME when there are no active child windows. An SDI program calls CFrameWnd::OnUpdateFrameMenu(), which uses CFrameWnd::m_hMenuDefault for its menu.

In both an SDI and MDI application, the menu you need to display is obtained from two other sources before using the m_hMenuShared or m_hMenuDefault menus. The framework first checks CFrameWnd::m_hMenuAlt. This is used in OLE supported programs with in-place activated objects that provide its own menus. The second source it checks is CDocument::GetDefaultMenu(), which is also virtual. The default implementation of it is to return NULL. This function can be overridden to allow the program to select the menu to display.

Changing the menus manually is done by calling OnUpdateFrameMenu() and DrawMenuBar(). You must call CFrameWnd::OnUpdateFrameMenu(NULL) or CMDIFrameWnd::OnUpdateFrameMenu(NULL) in order to set the new menu into the window. Call DrawMenuBar() to redraw the menu. Use the following code to implement for both MDI and SDI:

  1. Create a new menu resource (IDR_MYMENU1) in the resource editor.
  2. Add an HMENU data member to CMyDocument and override GetDefaultMenu() to return this data member:
       // .h file
       //    HMENU m_hMyMenu;
       //    virtual HMENU GetDefaultMenu(); // get menu depending on state
       HMENU CMyDocument::GetDefaultMenu()
       {
          return m_hMyMenu;    // just use original default
       }
    						
    Remember to initialize this member variable to NULL either in the constructor or CDocument::OnNewDocument().
  3. Change and redraw the menu at the desired times. For example, when switching between splitter panes, this is normally done in CView::OnActivateView(). The following code shows how to implement it in that function.
       // example within CView member function
       ((CMyDocument*)GetDocument())->m_hMyMenu = ::LoadMenu(
           AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_MYMENU1));
       ((CFrameWnd*)AfxGetMainWnd())->OnUpdateFrameMenu(NULL);
       AfxGetMainWnd()->DrawMenuBar();
Be sure to destroy the menu upon exiting the application, and avoid having too many menus loaded at once.

Menus can also be switched simply by changing the value of CMDIChildWnd::m_hMenuShared (MDI) or CFrameWnd::m_hMenuDefault (SDI). However, using GetDefaultMenu() allows a safer method of changing the menus.

For more information about how to share menus between MDI child windows, please see the following article in the Microsoft Knowledge Base:

118435 Sharing Menus Between MDI Child Windows.

REFERENCES

MFC Source Code.

Modification Type:MajorLast Reviewed:12/2/2003
Keywords:kbcode KB145857 kbAudDeveloper