65.9K
CodeProject is changing. Read more.
Home

An alternative way to create the menu bar

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3 votes)

Jul 22, 2011

CPOL

1 min read

viewsIcon

15764

An alternative way to create the menu bar

I don't like resources. I really don't. I can understand their usefulness for features such as string tables or embedded icons, but when it comes to the Windows Mobile menu bar definition, I just hate the whole thing. Take a good look at how the CrypSafe sample app's menu bar is defined:

ATL_IDW_MENU_BAR SHMENUBAR DISCARDABLE
BEGIN
IDR_MAINFRAME,
2,
I_IMAGENONE, ID_ACTION, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE,
ID_ACTION, 0, NOMENU,
I_IMAGENONE, ID_MENU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN  TBSTYLE_AUTOSIZE,
ID_MENU, 0, 0,
END

Intuitive, right? Incidentally, this resource is defined in the .rc2 file and the ATL_IDW_MENU_BAR identifier is used as a default parameter of the CreateSimpleCEMenuBar method of the CFrameWindowImplBase class template. So what do you have to do in order to change the menu bar? Gasp!

There is a better way, fortunately. A way that you can encapsulate and that makes the whole process a bit more transparent.

You create a Windows Mobile menu bar by calling the SHCreateMenuBar API typically from your main window's WM_CREATE handler. As you can see, the function takes a single parameter, a pointer to a SHMENUBARINFO structure where you define the menu bar. There are two ways to create a menu bar: you either specify a menu bar resource ID or you provide your own HMENU by specifying the SHCMBF_HMENU flag. This is in fact a much more palatable alternative because you can declaratively create your menu bar in a single location of your application's code. Using my beloved WTL, here's how the code would look like on the main frame's OnCreate handler:

if(m_mainMenu.CreateMenu())
{
  m_mainMenu.AppendMenu(MF_BYCOMMAND  MF_ENABLED  MF_STRING,
                        ID_BACK, _T("Back"));
  m_mainMenu.AppendMenu(MF_BYCOMMAND  MF_ENABLED  MF_STRING,
                        ID_MENU, _T("Menu"));

  SHMENUBARINFO mbi = { 0 };
  mbi.cbSize        = sizeof(mbi);
  mbi.hwndParent    = m_hWnd;
  mbi.dwFlags       = SHCMBF_HMENU;
  mbi.nToolBarId    = (UINT)(HMENU)m_mainMenu;
  mbi.hInstRes      = ModuleHelper::GetResourceInstance();
  mbi.nBmpId        = 0;
  mbi.cBmpImages    = 0;
  mbi.hwndMB        = NULL;

  BOOL bRet = ::SHCreateMenuBar(&mbi);
  if(bRet != FALSE)
  {
      m_hWndCECommandBar = mbi.hwndMB;
      SizeToMenuBar();
  }
}

The m_mainMenu variable is a CMenu, of course. The advantage of this approach is that you create the main menu bar in a very explicit way and if you later want to change it, you know where to find the code, and stop guessing what that crappy SHMENUBAR resource declaration means...