Taheri
Taheri

Reputation: 324

MFC SDI changed menu using SetMenu but how to change accelerators?

I have a SDI application. I want to change the menu based on certain conditions in run-time.

I use this code block to change the menu and it does it's job.

menu = GetMenu();
SetMenu(NULL);
menu->DestroyMenu();
menu->LoadMenu(IDR_MAINFRAM_2));
SetMenu(menu);

My only problem is that the accelerators for the new menu does not work. I already created an accelerator resource with ID IDR_MAINFRAM_2 but it did not solve my problem. What should I do to resolve this issue?

Upvotes: 3

Views: 558

Answers (3)

sergiol
sergiol

Reputation: 4335

May be your approach is simply not the best one. If you wanna alter the menu in run-time, my advice is for you to search on the Internet ON_WM_INITMENU and ON_WM_INITMENUPOPUP

The first applies to hard-coded menus of the Frame, the second is when you are using the MFC Feature Pack, and you have declared something a CMFCMenuBar in your frame class. Looking to your code, in a glance, it seems you are in the first case.

Upvotes: 0

Taheri
Taheri

Reputation: 324

Sorry for the late approval of the answer.

I actually used a workaround to overcome the issue and that was making sure the accelerators for different menus do not collide and let the accel table for the default menu to handle all accelerators. But I had to save menu state and avoid accelerator keys of other menus when the respective menu item was not showing.

Well that was hardly a good workaround. But I got it to work.

Today I tried to validate the answers I received to accept the correct one. The summary is:

I did not need the call to TranslateAccelerator that IInspectable stated. Loading accelerators the way Vlad suggested does the trick, BUT there is an assertion fail when you call LoadAccelTable as it could be read from the documentation for this function which states that this function should be called only once.

So I overcame it by calling DestroyAcceleratorTable function and setting CFrameWnd::m_hAccelTable to NULL. before calling LoadAccelTable.

Also a sub note for those who want to change menus of SDI or MDI applications the way I did. That also cause a ,don't know why, assertion failed that was overcame by using the method suggested in this article.

To sum it up, in case the provided link becomes unavailable, it says:

  1. Create a new menu resource (IDR_MYMENU1) in the resource editor.
  1. 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().

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

Also make sure not to destroy the default menu:

if (m_hMenuDefault != GetMenu()->m_hMenu)
    GetMenu()->DestroyMenu();

Upvotes: 0

Vlad Feinstein
Vlad Feinstein

Reputation: 11321

This is straight forward. Assuming you do your manipulation from the CFrameWndEx-derived class, just call:

LoadAccelTable(MAKEINTRESOURCE(IDR_MAINFRAM_2));

MFC will process that accelerator table for you

Upvotes: 1

Related Questions