Introduction
Microsoft introduced a new feature some Office versions ago: Popup menus you
can tear off and use as toolbars. This is a good way for the user to put
important functions directly on the application workspace without opening a menu
each time.
Trying to implement this feature caused a lot of problems. The easiest thing
was to draw the menu so it looks like you can tear it off. The next thing was
how to get the mouse movements and button pressed, which caused me to stop
working on this for a long time. In the November 2003 issue of the MSDN magazine
Paul DiLascia wrote some code to display menu tooltips. With this code it was
quite easy to get it working.
Behind the scenes
The new class
CMenuTearOffManager has all
the functions you need for tear-off menus. It basically maps popup menus to
toolbars and handles the GUI part. Using Paul DiLascia
CSubclassWnd
class
CMenuTearOffManager reacts on every new popup menu and checks
(and modifies) it in case it has a tear-off item. Then the "caption" is
calculated and painted. Let's see how it works inside: First, whenever a new
popup menu is opened (
WM_INITMENUPOPUP) it goes through all items
and compares them to the internal map that assigns menu items to toolbars. In
case this item is found it is converted to an ownerdrawn item. For that item
WM_MEASUREITEM calculates the height for the "caption" and
WM_DRAWITEM paints the caption. And here's the tricky part: during
painting a new window is created that lays above the menu item, so mouse moves
an be trapped; each time you select a different menu item the window gets
invisible. The new window is not active (which would cause the menu to close),
so mouse moves are captured by
WM_NCHITTEST. Moving the mouse and
pressing the left mouse button closes the window and forwards the message to the
toolbar control - that's it!
Using the code
First, add the
CMenuTearOffManager class to
the
CMainFrame. In your
CMainFrame::OnCreate() add all
tear-off toolbars using code like this:
if (!m_wndToolBarTearOff1.CreateEx(this, TBSTYLE_FLAT, WS_CHILD
|WS_VISIBLE| CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBarTearOff1.LoadToolBar(IDR_TEAROFF1))
{
TRACE0("Unable to create toolbar 1\n");
return -1;
}
m_wndToolBarTearOff1.EnableDocking(CBRS_ALIGN_ANY);
ShowControlBar(&m_wndToolBarTearOff1, FALSE, FALSE);
(The last line of code causes the toolbar to be initially invisible)
Then insert a new menu item on top of each popup you'd like to act as a
tear-off menu (here: ID_TEAROFFDEMO_TEAROFF2). Also insert new
toolbars that are displayed for the popup menus. There's no need to have the
same commands on the toolbar.
After creating all toolbars use these lines to install the class and connect
it to menu items:
m_menuTOManager.Install(this, this);
m_menuTOManager.AddTearOff(ID_TEAROFFDEMO_TEAROFF1, &m_wndToolBarTearOff1);
m_menuTOManager.AddTearOff(ID_TEAROFFDEMO_TEAROFF2, &m_wndToolBarTearOff2);
...
This connects the "virtual" menu item
ID_TEAROFFDEMO_TEAROFF2
to the toolbar
m_wndToolBarTearOff2. All the rest is done
automatically.
Please see the sample how to use it. All comments are in English, only the
resources are German.
Limitations
This class has a few differences to the "original"
implementation you know from e.g. Microsoft Office and some other limitations:
- Different drawing of the tear-off caption
- Menu closes immediately after tearing the toolbar away
- Office products allow to doc the popup again during dragging
- Not (yet) possible for context menus
- Not tested for ownerdrawn menus, but it should work
Licence
Feel free to use this class in any of your projects or modify
it! Please keep my credits in the source.
Credits
Paul DiLascia for his subclass class and the menu tip manager, which helped a
lot developing this class!
History
- 30.11.2003: Initial release