Visual Studio/Office 97 style Flat Toolbar and Dockable Menu bar (2)

 
Download Source Code and Example 200KB


This article is based on Visual Studio/Office 97 style Flat Toolbar and Dockable Menu bar from Tony Hoyle .

Like many people, I wanted to give to my applications the fancy look from Visual Studio/Office 97. As a frequent visitor of CodeGuru, I saw and tried the miscellanous sources related to that subject.

What I liked in Tony's code is its shortness, but for myself his code have two major problems:

  • Sub-menus couldn't be shown
  • All the menu navigation has to be simulated.

Basically, what's happening in Tony's code:

In your FrameWnd you call the CNGenericMenu::SetMenu function to load the menu. Then, when the user click/Shortcut a menu, a Notify Reflect on the CNGenericMenu::OnShowMenu is triggered, this function then call the CNGenericMenu::ShowDropDown.

This last function, extract a sub-menu from the menu title, and sends it to CNGenericPopup::ShowMenu. This function in turn, is responsible for creating a window in which the menu will be drawn/emulated.

In fact, what must be done there is to change the place where the menu has to be painted, and if possible to use a standard behaviour.

For our help, the CMenu class has a TrackPopupMenu function. This function is used normally to show a contextual popup-menu when the user is using the Right click on any object. For our help, there are no differences between the normal and contextual menus. So what has only to be done there, is to call it with the hMenu given by CNGenericMenu::ShowDropDown.

After all this, the ShowMenu function becomes:


BOOL CNGenericPopup::ShowMenu(BOOL bFromKey, int x, int y, CWnd* pParent, HMENU hMenu)
{
	ASSERT(hMenu);

	HideMenu();
	m_pMenu->m_hMenu=hMenu;
	m_pParent=pParent;

	if ((FALSE == m_bIsAttached) && (TRUE == m_cSubMenu.Attach (m_pMenu->m_hMenu)))
	{
		m_cSubMenu.TrackPopupMenu (TPM_LEFTALIGN , x , y , m_pParent , NULL);
		m_pParent->SendMessage(WM_ENTERMENULOOP,FALSE);
		m_bIsAttached = TRUE;
	}

	if(bFromKey)
	{
		m_pParent->SendMessage (WM_MENUSELECT , 0 , (LPARAM)hMenu);
	}

	return TRUE;
}

Then we arrived on an other problem: when the user, goes from one menu to an other, we have to close the opened menu before creating the new one. To do it, the CMenu is no help for us. We have to use directly the different menu events.

If you use the Spy++ from the Developper Studio, you'll see that the WM_EXITMENULOOP event is sent to close the currently opened menu; so that's exactly what we are going to do. The only thing that we must take care on is to Detach the hMenu, prior to sending the WM_EXITMENULOOP event.

So the HideMenu function becomes:


BOOL CNGenericPopup::HideMenu()
{
if (m_bIsAttached == TRUE)
{
m_cSubMenu.Detach ();
m_pParent->SendMessage(WM_EXITMENULOOP,(WPARAM)FALSE,0);
m_bIsAttached = FALSE;
}
return TRUE;
}

Finally, the last problem that subsists is when we click on the header of a menu to show the menu, then click on it again to close it. To have the correct behaviour, we must change slitly the CNGenericMenu::ShowDropDown function, and in it to force the close of the submenu by calling explicitely the HideMenu function. On the same occasion, we can suppress the ON_DISMISS event which now isn't used any more.

By doing things that way, we are now able to use all the different classes that you can find on this site to enhance the display of your menus and sub-menus, and have a good lightweight framework to base your work on.

Last updated: 03 June 1998