This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


January 1998

Microsoft Systems Journal Homepage

Give Your Applications the Hot New Interface Look with Cool Menu Buttons

Paul DiLascia

Download ROPview.exe (288KB)

Paul DiLascia is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or http://pobox.com/~askpd.

In one of my recent columns, I showed you how to implement the new flat-style toolbars and coolbars found in the Office 97 products, like Microsoft Outlook and Visual Studio 97 (MSJ, August 1997). At the end of the column, I made a crack that everyone would want the new menus next. Well, sure enough, many of you sent email asking how to add buttons to your menus. Naturally I was excited by the prospect of writing code that people were clamoring for.
Implementing the buttons seemed like an easy enough task—and, in the Ory, it was. Kinda sort of. It ended up over a thousand lines. Along the way I ran into so many quirks, pitfalls, and oddities that I decided the subject deserved a full article. If nothing else, it will amaze and amuse you to see how bizarre Windows® can be. Amusing, that is, if you can somehow manage to keep from going postal.

GUI Fashion Rage
It seems that every couple of years, Windows-based programs get a new look. Someone comes out with a new idea like toolbars or 3D controls or Tommy Hilfiger prop sheets (just kidding) and everyone else has to follow suit. The latest ne plus ultra in GUI couture is what I call the cool look (for lack of a better term), since it includes the coolbars in the comctl32.dll that ships with Microsoft Internet Explorer 3.0. (See Strohm Armstrong's article in the October and November 1996 issues, and my August and October 1997 C++ Q&A columns.) The Microsoft Office 97 products and Visual Studio 97 have their own homegrown coolbars.
Figure 1  ORPC Messages
Figure 1

In addition to coolbars, the new look incorporates menus with button bitmaps (see Figure 1). The idea is that showing the bitmap next to the command helps users learn which buttons perform which commands—the same way menus show accelerator names to help you learn keyboard shortcuts. I'm not sure whether cool menus are cool; to me it seems like what my graphic designer friends call screen junk. But then I thought the same thing about toolbars when they first came out. (Screen junk is a highly technical term—for an example, see Figure 2.)


Figure 1  ORPC Messages
Figure 2 Screen Junk

Now I'm willing to bet a nickel that Microsoft will eventually incorporate cool menus into a new DLL, but for those of you who simply can't bear to be seen in anything less than up-to-the-minute GUI fashion chic, I'll show you how to implement cool menus ASAP. My cool menu manager can be installed in less time than it takes to compile your app, or in about thirty seconds if you type fast. Really.

Cruising the Cool Menu Manager
CCoolMenuManager is a C++ class I wrote to do cool menus in MFC apps. To use it, all you have to do is plop an instance in your frame window and call two functions:

 class CMainFrame : public CFrameWnd {
    CCoolMenuManager m_menuMgr;
 •
 •
 •
 };
 // (in your CMainFrame::OnCreate)
 m_menuMgr.Install(this);
 m_menuMgr.LoadToolbar(IDR_MAINFRAME);
That's it. That's all there is! The toolbar need not be displayed, or even instantiated as a CToolBar object. It only has to exist in your resource file. If you have more than one toolbar, you can call LoadToolbars (note the s).
 static UINT toolbars[] {
    ID_TOOLBAR_FILE,
    ID_TOOLBAR_EDIT,
    ID_TOOLBAR_FORMAT,
 };
 m_menuMgr.LoadToolbars(toolbars, 3);
I wrote LoadToolbars because it makes me weep when I see code like this:
 menuMgr.LoadToolbar(IDR_TOOLBAR1);
 menuMgr.LoadToolbar(IDR_TOOLBAR2);
 •
 •
 •
 
 menuMgr.LoadToolbar(IDR_TOOLBAR27);
That kind of repetitive, open coding typical of SDK sample programs is one reason there are apps that take over 80MB on disk. It announces to anyone who reads your code: "I am an ignoramus."
In any case, you can see how easy CCoolMenuManager is. Just create an object and call two functions. In addition to that basic picture, CCoolMenuManager offers some extra features that I'll discuss a bit later. For example, there's a flag that lets you turn the buttons on or off:
 // turn menu buttons off
 m_menuMgr.m_bShowButtons = FALSE;
I used m_bShowButtons in my demo program ROPView (described later) to implement a View | Hide Menu Buttons command. This will be well-appreciated by grumpy hackers like me who think retro is the ultimate GUI fashion statement. (Soon, the world will catch up to my advanced taste—I predict that before the year 2005, Windows will revert to its 3.1 look.)

Inside CCoolMenuManager
If all you want is buttons in your menus, you can stop reading, download the code from MSJ, add the four lines to your app, and compile. Then tell your boss you spent hours working late and deserve a raise. But if you're the type who takes your toys apart, keep reading. I'll show you how CCoolMenuManager works. As I said earlier, it's quite amusing. Plus, there are plenty more goodies in store.
The basic strategy behind CCoolMenuManager is simple. When the user invokes a menu, CCoolMenuManager converts every item to an owner-draw item that draws itself exactly the same way Windows does—using the same font, colors, and so on—with the added feature of drawing a bitmap in the left margin of every command that has a toolbar button. When the user is finished with the menu, CCoolMenuManager converts all the items back to their original state. Like I said, simple. But the problem with Windows isn't understanding what you want to do, it's getting Windows to understand, too.
Let's begin when your app calls LoadToolbar. (Figure 3 shows the source code so you can follow along.) LoadToolbar loads the bitmap first, then the toolbar. Remember, a toolbar resource is actually two resources with the same ID: a bitmap and a toolbar. Each step requires a trick.
The first trick is converting the bitmap from gray to the current 3D color scheme. MFC does this with the function AfxLoadSysColorBitmap. It converts the four shades—white, gray, dark gray, and black—to the current 3D system colors. MFC uses this function in its own CToolBar::LoadToolbar, but since AfxLoadSysColorBitmap requires module instance and resource handles, which I'm too lazy to get, I wrote a wrapper that lets you give just the resource ID.

 HBITMAP hbmToolbar = 
    PxLib::LoadSysColorBitmap(nID);
PxLib is a namespace I use for helper functions like the Se to avoid name collisions.
The second trick to loading toolbars is knowing what a toolbar resource looks like in memory. This you can discover from bartool.cpp in Figure 3.
 // RT_TOOLBAR resource
 struct TOOLBARDATA {
    WORD wVersion;           // should be 1
    WORD wWidth;             // bitmap width
    WORD wHeight;            // height 
    WORD wItemCount;         // num items
    WORD items[wItemCount];  // item IDs
 };
The actual size of the TOOLBARDATA is variable; the items array has wItemCount items. The nth word in the array is the command ID for the nth button in the bitmap, or zero for a...

From the January 1998 issue of Microsoft Systems Journal.