Objective Toolkit : Chapter 7 Menu Bars : Using the Menu Bar Classes
Using the Menu Bar Classes
The following sections give details and tips on using the menu bar classes.
To Incorporate Objective Toolkit Menubars Into Your Code—Simple Case
1. Instantiate the menubar object in your mainframe constructor in addition to the SECToolBarManager object created for customizable toolbar support. If you’re creating a SDI application, use SECMenuBar. If you’re creating an MDI application, use SECMDIMenuBar.
NOTE >> m_pMenuBar is a member variable of SECFrameWnd or SECMDIFrameWnd (base class of CMainFrame).
 
CMainFrame::CMainFrame()
{
m_pMenuBar = new SECMDIMenuBar; // or SECMenuBar for SDI
}
2. If you want to have bitmap support enabled for your menubar menus, include a call to the EnableBmpMenus() method in the constructor.
3. Delete the menubar in the main frame destructor.
 
CMainFrame::~CMainFrame()
{
if(NULL != m_pMenuBar)
{
delete m_pMenuBar;
m_pMenuBar = NULL;
}
}
4. In your mainframe OnCreate() handler, use the SetMenuInfo() method of SECToolBarManager to define the menu resources used by the application. This member takes a variable number of arguments based on the number of toolbars to autoconfigure. The syntax is:
 
SetMenuInfo(<count>,<menu id 1>,<menu id 2>,...,<menu id n>);
For example:
 
pMgr->SetMenuInfo(4,IDR_MAINFRAME,IDR_EDITVIEW, IDR_LISTVIEW, IDR_FILEVIEW);
Internally these functions assign a unique bit flag to each menu resource. The code in the preceding example assigns the bit flags in the following manner:
Menu Resource
Bit Mask
IDR_MAINFRAME
0x00000001
IDR_EDITVIEW
0x00000002
IDR_LISTVIEW
0x00000004
IDR_FILEVIEW
0x00000008
Then, the menu bar is populated with SECTBMenuBtn instances for all of the menu resources. Each SECTBMenuBtn is assigned the bit flag for its parent menu resource. In the preceding example, all menu buttons belonging to IDR_MAINFRAME would have bit 0 set in their bit flag, menu buttons belonging to IDR_EDITVIEW would have bit 1 set, etc.
When a MDI child window becomes active, the SECMenuBar::SwitchMenu() function is called with the ID of the menu resource associated with that window (taken from the document template). SwitchMenu() looks up the bit flag associated with the given ID, and shows all the SECTBMenuBtns that have that bit set and hides those that do not with the button style TBBS_HIDDEN.
5. If you want to apply the cool look to your menubar, call SECToolBarManager::EnableCoolLook() in your mainframe's OnCreate() handler For example:
 
pMgr->EnableCoolLook();
6. Call EnableDocking(), as you would for any standard toolbar. This step assumes that you called EnableDocking(CBRS_ALIGN_ANY) on the frame window. For example:
 
// dock the menubar
m_pMenuBar->EnableDocking(CBRS_ALIGN_ANY);
7. Dock the menu bar using either the DockControlBar() or DockControlBarEx() methods. You can float the menu bar afterward, but it must be docked for initialization.
 
DockControlBar(m_pMenuBar);
To Incorporate Objective Toolkit Menubars Into Your Code--Advanced Case
You can make the menu bar more efficient by using the toolbar manager (see the MDIADV sample). With the button map, you can define the bit flags that specify to which menu a particular menu button belongs. This enables you to use the toolbar manager to create a single bar that shows and hides the buttons instead of multiple bars that you need to switch between. You can still use the SwitchMenu() function; however, an additional button map allows greater customization. When you create a button map fewer buttons are created which results in a reduction of the memory required.
 
// IDs for menu buttons
#define ID_MENUBAR_FILE 0x80000001
#define ID_MENUBAR_EDIT 0x80000002
#define ID_MENUBAR_VIEW 0x80000003
#define ID_MENUBAR_TOOLS 0x80000004
#define ID_MENUBAR_WINDOW 0x80000005
#define ID_MENUBAR_HELP 0x80000006
 
// View Bit Flags
#define BITFLAG_MAINFRAME 0x00000001
#define BITFLAG_EDITVIEW 0x00000002
#define BITFLAG_ALL BITFLAG_MAINFRAME | \
BITFLAG_EDITVIEW
 
 
//////////////////////////////////////////////////////
// Define the button map
// NOTE: MENU_BUTTON_EX and MENU_BUTTON are macros. Line
// continuation
// characters are required if the text is to be used as formatted
// here. They may be removed if your macro only uses one line.
BEGIN_BUTTON_MAP(btnMap)
MENU_BUTTON_EX(ID_MENUBAR_FILE, SEC_TBBS_NODISABLE, \
IDR_MDIADVTYPE, 0, _T("&File"), \
BITFLAG_ALL)
MENU_BUTTON_EX(ID_MENUBAR_EDIT, SEC_TBBS_NODISABLE, \
IDR_MDIADVTYPE, 1, _T("&Edit"), \
BITFLAG_EDITVIEW)
MENU_BUTTON_EX(ID_MENUBAR_VIEW, SEC_TBBS_NODISABLE, \
IDR_MDIADVTYPE, 2, _T("&View"), \
BITFLAG_ALL)
MENU_BUTTON_EX(ID_MENUBAR_TOOLS, SEC_TBBS_NODISABLE, \
IDR_MDIADVTYPE, 3, _T("&Tools"), \
BITFLAG_ALL)
MENU_BUTTON_EX(ID_MENUBAR_WINDOW, SEC_TBBS_NODISABLE, \
IDR_MDIADVTYPE, 4, _T("&Window"), \
BITFLAG_EDITVIEW)
MENU_BUTTON_EX(ID_MENUBAR_HELP, SEC_TBBS_NODISABLE, \
IDR_MDIADVTYPE, 5, _T("&Help"), \
BITFLAG_ALL)
END_BUTTON_MAP()
Now you have two different menus: one for when no view is available (IDR_MAINFRAME), and the other for an edit view (IDR_EDITVIEW). You have declared that the menu buttons ID_MENUBAR_FILE, ID_MENUBAR_VIEW, ID_MENUBAR_TOOLS, and ID_MENUBAR_HELP appear in both menus by specifying the BITFLAG_ALL bit flag. You set ID_MENUBAR_EDIT and ID_MENUBAR_WINDOW to appear only on the IDR_EDITVIEW menu by specifying the BITFLAG_EDITVIEW bit flag. Although BITFLAG_MAINFRAME is defined, it is only used by the BITFLAG_ALL definition.
In your CMainFrame::OnCreate() method, use the SECToolBarManager::SetMenuMap() method to define how the bit flags map onto menu resource IDs, and also use the SECToolBarManager::LayoutMenuBar() to define the initial menu button layout for the menu bar.
You need to create a table that specifies the order of the buttons on the menu bar.
 
// Table mapping bitflag to menu resource
// Default menu bar layout
static UINT menuButtons[] =
{
ID_MENUBAR_FILE,
ID_MENUBAR_EDIT,
ID_MENUBAR_VIEW,
ID_MENUBAR_TOOLS,
ID_MENUBAR_WINDOW,
ID_MENUBAR_HELP
};
Then, you need to associate each menu type with a bit flag.
 
static SECMenuMap menuMap[] =
{
{ IDR_MAINFRAME, BITFLAG_MAINFRAME },
{ IDR_MDIADVTYPE, BITFLAG_EDITVIEW }
};
The following code is not complete. It only shows the steps necessary to implement the menu bar. The code that implements toolbars, status bars, and other objects in the main frame is not shown. This example assumes that simple persistence is implemented with storage under a key named Menu60.
 
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (SECFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// Verify the presence of the toolbar manager and cast it to
// the correct type so that we can access the proper methods.
ASSERT(m_pControlBarManager != NULL);
ASSERT_KINDOF(SECToolBarManager, m_pControlBarManager);
SECToolBarManager* pToolBarMgr =
(SECToolBarManager*)m_pControlBarManager;
 
// Set the menu map for the toolbar manager using our
// previously defined menuMap.
pToolBarMgr->SetMenuMap(menuMap, NUMELEMENTS(menuMap));
// Use the previously defined menu button layout to set the
// order and presence of the default buttons.
pToolBarMgr->LayoutMenuBar(NUMELEMENTS(menuButtons), menuButtons);
 
// Allow docking on all four sides.
EnableDocking(CBRS_ALIGN_ANY);
// Enable the cool look. Cool look is required to obtain
// the Office 2003 and Vista Classic look and feel.
pToolBarMgr->EnableCoolLook(TRUE);
 
// load old state (if any)
LoadBarState(_T("Menu60"));
pToolBarMgr->LoadState(_T("Menu60"));
 
return 0;
}
When an SECMDIChildWnd is activated, it calls into SECMDIFrameWnd::ActivateMenu() with the ID of its menu resource. You can override this function to customize the way the menu bar is changed when views become active. You can change the state of the menu bar by calling SECMenuBar::SwitchMenu() or SECMenuBar::EnableBitFlag() to enable the buttons associated with the given menu ID or bit flag respectively.
SECMenuBar::SwitchMenu() is called with the menu ID of the menu you want to display.
 
BOOL SECMenuBar::SwitchMenu(UINT nIDResource)
SECMenuBar::EnableBitFlag() is called with the bitflag and a BOOL bUpdate. If you wish to have the menu bar redrawn immediately, you can pass TRUE in for bUpdate.
 
void SECMenuBar::EnableBitFlag(DWORD dwBit, BOOL bUpdate)
To Implement SECMenuBar Or SECMDIMenuBar Without a Toolbar Manager
The following steps provide dockable cool look menus without customization.
1. Follow the steps for enabling docking window support. This consists of replacing your frame classes (SECFrameWnd) with SECMDIFrameWnd, replacing the child frames with SECMDIChildWnd, and changing all dockable and non-dockable bar types to the Stingray equivalent. If persistence is needed, instantiate a toolbar manager.
2. Instantiate the menubar object in your mainframe constructor. If you are creating an SDI application, use SECMenuBar. If you are creating a MDI application, use SECMDIMenuBar.
NOTE >> m_pMenuBar is a member variable of the base class.
 
CMainFrame::CMainFrame()
{
m_pMenuBar = new SECMDIMenuBar; // or SECMenuBar for SDI
}
3. Delete the menubar in the main frame destructor.
 
CMainFrame::~CMainFrame()
{
if(NULL != m_pMenuBar)
{
delete m_pMenuBar;
m_pMenuBar = NULL;
}
}
4. In MainFrame::OnCreate(), create the menubar. It is created in a manner similar to the other controlbars. For example:
 
// SDI:
if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS |
CBRS_EX_GRIPPER, this) ||
!m_pMenuBar->LoadMenu(IDR_MAINFRAME))
{
TRACE0("Failed to create menubar\n");
return -1;
}
 
// MDI, use SetMenuInfo instead of LoadMenu:
if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER, this)
|| !m_pMenuBar->SetMenuInfo(2, IDR_MAINFRAME, IDR_MDI2TYPE))
{
TRACE("Failed to create menubar\n");
return -1;
}
5. Call EnableDocking() as you would a normal toolbar. This step assumes that you called EnableDocking(CBRS_ALIGN_ANY) on the frame window. For example:
// dock the menubar
m_pMenuBar->EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(m_pMenuBar);
To remove the close button from a floating menu
As the last statement in the OnCreate() method of your main frame, use the SECMenuBar::ModifyStyle() method to remove the WS_SYSMENU style flag. For example:
 
m_pMenuBar->ModifyStyle(WS_SysMenu,NULL);
To switch between menus
SDI Implementation Method
1. In the OnCreate() method of the main frame, replace the standard call to SECMenuBar::LoadMenu() with a call to SECMenuBar::SetMenuInfo(). Make sure you define every menu resource you want to use. For example, the following code defines a menubar that uses two menu resources, IDR_MAINFRAME and IDR_MENU2:
 
if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER,
this,
WS_VISIBLE | WS_CHILD | CBRS_TOP,
AFX_IDW_CONTROLBAR_LAST) ||
!m_pMenuBar->SetMenuInfo(2, IDR_MAINFRAME, IDR_MENU2))
{
TRACE0("Failed to create menubar\n");
return -1;
}
2. In the OnCreate() method of the main frame, call the SECMenuBar::SwitchMenu() function to activate the correct initial menu directly after the calls to DockControlBar(). For example, the following code activates the IDR_MAINFRAME menu:
 
m_pMenuBar->SwitchMenu(IDR_MAINFRAME);
3. To toggle the menu at run time, call the SECFrameWnd::SwapMenu() method. For example, the following code activates the menu IDR_MENU2:
 
SwapMenu(IDR_MENU2);
MDI Implementation Method
1. In the OnCreate() method of the main frame, ensure that the call to SECMenuBar::SetMenuInfo() defines all the menus that the application can use. For example, the following code defines that the menubar uses four menu resources— IDR_MAINFRAME, IDR_MAINFRAME_MENU2, IDR_MDITYPE and IDR_MDITYPE_MENU2.
 
if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER,
this,
WS_VISIBLE | WS_CHILD | CBRS_TOP,
AFX_IDW_CONTROLBAR_LAST) ||
!m_pMenuBar->SetMenuInfo(4,
IDR_MAINFRAME,
IDR_MAINFRAME_MENU2,
IDR_MDITYPE,
IDR_MDITYPE_MENU2))
{
TRACE0("Failed to create menubar\n");
return -1;
}
2. Directly after this, add a call to SECMDIFrameWnd::LoadAdditionalMenus(). This method informs the menubar about menu resources that are not loaded by the document templates or the frame window’s menu. For example, the following code loads the IDR_MAINFRAME_MENU2 and IDR_MDITYPE_MENU2 menus by default. The menu IDR_MDITYPE is loaded automatically by the document template, and IDR_MAINFRAME is loaded automatically by the frame window so they are not defined here.
 
if(!LoadAdditionalMenus(2,IDR_MAINFRAME_MENU2,
IDR_MDITYPE_MENU2))
{
TRACE0("Failed to load additional menus\n");
}
To toggle the menus of the frame window or MDI child windows, call the SECMDIFrameWnd::SwapMenu() and SECMDIChildWnd::SwapMenu() methods. For example, the following activates the menu IDR_MDITYPE_MENU2.
 
SwapMenu(IDR_MDITYPE_MENU2);
To use bitmap menus without the cool-look toolbars
1. Replace your mainframe base class with SECFrameWnd for SDI or SECMDIFrameWnd for MDI. If applicable, change your childframe base class to SECMDIChildWnd.
2. In your mainframe constructor, call EnableBmpMenus().
 
CMainFrame::CMainFrame()
{
EnableBmpMenus();
}
3. Configure an appropriate toolbar bitmap resource to supply the bitmap resources in your CMainFrame::OnCreate().
 
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (SECMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// All other init...
// Use the IDR_MAINFRAME toolbar resource for the
// bitmap menus
AddBmpMenuToolBarResource(IDR_MAINFRAME);
}
For more information, see “To use bitmap menus in context menus.”
To use bitmap menus in context menus
With Main Frame Message Processing
By default, bitmaps are not displayed in context popup menus because the bitmaps are added to the menus via a plug-in on the mainframe. If the WM_INITMENUPOPUP is handled by the view, there is no opportunity to add the bitmaps.
To circumvent this, parent the menu to the mainframe. This allows the bitmap menu plug-in to process the WM_INITMENUPOPUP the same way it processes the menus from the menubar, which makes adding bitmap support much simpler. For example:
 
void CMyView::OnRButtonDown(UINT nFlags, CPoint point)
{
CMenu menuText;
 
// Load the menu
menuText.LoadMenu(IDR_MAINFRAME);
 
// Pick the popup you wish to use. You may add and
// delete menu items at this point.
CMenu* pMenuPopup = menuText.GetSubMenu(0);
 
// This puts the menu at the right spot on the screen.
ClientToScreen(&point);
 
// Notice that this is parented to the mainframe. The
// TMP_XXX style could be any of the valid ones.
pMenuPopup->TrackPopupMenu( TPM_RIGHTALIGN , point.x, point.y,
AfxGetMainWnd());
}
Without Main Frame Message Processing
Unfortunately, this has the effect of routing all context message commands directly to the mainframe. If you want to re-parent the context menu as a child of the view so you can route the commands to the parent view instead, complete the following steps.
1. Declare a pointer to an SECBmpMenuPlugIn object in your view header.
 
protected:
SECBmpMenuPlugIn* m_pBmpMenuPlugin;
2. Initialize this pointer to NULL in your class constructor.
3. In your CView::OnInitialUpdate(), initialize the bitmap menu plugin, defining the toolbar resource from which to load the bitmap images.
 
CMyView::OnInitialUpdate()
{
// Initialize the bitmap menu plugin
m_pBmpMenuPlugin=new SECBmpMenuPlugIn;
m_pBmpMenuPlugin->PlugInTo(this);
m_pBmpMenuPlugin->AddToolBarResource(IDR_MAINFRAME);
}
4. Override WindowProc() and then add the following code to forward events to the plugin.
 
LRESULT CMyView::WindowProc(UINT message, WPARAM wParam,
LPARAM lParam)
{
// Plug the bitmap menu plugin into this window's WNDPROC
if(m_pBmpMenuPlugin)
{
if(message==WM_INITMENUPOPUP)
m_pBmpMenuPlugin->InitMenuPopup(wParam,lParam);
else
{
LRESULT result = 0;
m_pBmpMenuPlugin->OnWndMsg( message, wParam,
lParam, &result );
if( m_pBmpMenuPlugin->m_bExitMessage )
return result;
}
}
return CView::WindowProc(message, wParam, lParam);
}
5. Delete the m_pBmpMenuPlugin object in your view destructor with the following code:
 
if(m_pBmpMenuPlugin) delete m_pBmpMenuPlugin;