Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for


Advanced Search
MSDN Home > MSJ > August 1997
August 1997


Code for this article: CQA0897.exe (67KB)
Paul DiLascia is a freelance software consultant specializing in training and software development in C++ and Windows. He is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992).

Q I'm building a Web app and want to implement the flat-style buttons in my toolbar like the buttons in Internet Explorer. I read about the Internet Explorer 4.0 (IE 4.0) common controls in the November 1996 issue of MSJ, but I'm using MFC and I can't figure out how to get the flat style with CToolBar.
From numerous readers

A One of the hallmarks of a true hacker is being the first to give your app the new look—whether it's shaded caption bars, 3D windows (as opposed to the old, flat Windows® 3.1 look), or the latest UI fashion: flat-style toolbars and coolbars. (See the next question for details on coolbars.) As soon as Microsoft—or anyone else—comes out with a new look, everyone races to jump on the bandwagon lest their app seem declassé. Prediction: a few years from now, the old Windows 3.1 look will make a retro comeback, along with saddle shoes and bobbysocks. You heard it here first.
Now, to answer the question. Microsoft recently introduced several new and enhanced common controls (see "Previewing the New and Improved Common Controls DLL for Microsoft
® IE 4.0" by Strohm Armstrong, in the October and November 1996 issues of MSJ). Although the new controls were designed for IE 4.0, they reside in a separate DLL, COMCTL32.DLL, that any app can call. Visual C++® 5.0 comes with an updated commctrl.h #include file that has all the definitions you need to use the new controls.
Figure 1: Toolbar with TBSTYLE_FLAT
Figure 1: Toolbar with TBSTYLE_FLAT

Believe it or not, to get the flat-style toolbar you see in Figure 1, all you have to do is add one line to your code. That's right, one line.
 m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT);
The place to put it is in CMainFrame::OnCreate, after creating your toolbar. TBSTYLE_FLAT is a new toolbar style that produces the look in Figure 1. Note that you must use CWnd::ModifyStyle to modify the style; you can't specify TBSTYLE_FLAT when creating the toolbar.
 // This fails
 m_wndToolBar.Create(this, WS_CHILD|WS_VISIBLE|
                     CBRS_TOP|TBSTYLE_FLAT);
If you try passing TBSTYLE_FLAT to CToolBar::Create, it won't work because MFC clobbers your TBSTYLE_FLAT flag. The hex value for TBSTYLE_FLAT (and other new toolbar style flags) conflicts with special CBRS_xxx control bar styles MFC uses for its own purposes. For example, MFC lets you specify CBRS_TOP for a top-aligned toolbar, or CBRS_BORDER_BOTTOM to get a border along the bottom of your toolbar. But CBRS_BORDER_BOTTOM and TBSTYLE_FLAT have the same value, 0x0800. In gen- eral, MFC uses the low-order word (16 bits) of the style for the CBRS_ flags, which conflict with the TBSTYLE_ flags.
The folks in Redmond wrote CControlBar long before ToolbarWindow32 and TBSTYLE_FLAT were even a glint in Bill's eye, but they must've had some idea about possible future conflicts because CToolBar::Create contains the line
 dwStyle &= ~CBRS_ALL;
just before MFC passes dwStyle to CWnd::Create. That is, MFC turns off the low-order 16 style bits before creating the ToolbarWindow32. Personally, I find this quite amusing. The MFC programmers did just what Microsoft says you're never supposed to do—use unused style bits for your own purposes. In any case, the upshot is TBSTYLE_FLAT gets clobbered if you pass it to CToolBar::Create. This is why the only way to set the new style is to call CWnd::ModifyStyle after you've created the toolbar. On the bright side, anything that lets you get a whole new UI look with just one line of code can't be too bad.
There's just one little problem (there's always "one little problem"). If your toolbar has the docking feature, the order of the universe spins slightly out of whack. If you implement the one-liner as I have indicated, and if you also call the MFC EnableDocking functions to create a docking toolbar, you will discover—to your great dismay—that the toolbar doesn't repaint itself properly when you drag it around to do the docking thing. Figure 2 shows what I mean. Yech! The problem is that ToolbarWindow32 achieves its flat look by drawing the button bitmaps transparently, which MFC doesn't expect. MFC expects the toolbar to paint the gray background as well as the buttons. So now when the user moves the docking toolbar to a new location, it leaves a shadow of itself in the old location, as you see in Figure 2.
Figure 2:  Parlez-vous display bug?
Figure 2: Parlez-vous display bug?

A "correct" solution is difficult to achieve because it requires overriding several MFC functions, and ideally you'd have to modify the MFC source code. A full explanation is pointless and would only put you to sleep. (Besides, I can't figure out what those kids were thinking about half the time.) Fortunately, there is a simple fix: derive a new toolbar class that handles WM_WINDOW–POSCHANGING by repainting the appropriate areas whenever the user moves the toolbar. This is admittedly a bit of a kludge, but it works like a charm—and it's impossible to program Windows or MFC without kludges.
Figure 3 shows a CFlatToolBar class I wrote that does flat toolbars correctly. To use it, all you have to do is change CToolBar to CFlatToolBar in your MainFrm.h. Of course, you have to #include FlatBar.h and add FlatBar.cpp to your project, too.
There is one minor design glitch in CFlatToolBar: I wanted to make the Create function modify the style, but I discovered that this won't work because the style must be modified after loading the toolbar, or else the buttons will come out aligned to the very top edge of the toolbar. Go figure. (I guess Microsoft is still working out the bugs on this one.) So instead of writing CFlatToolBar::Create, I overloaded the LoadToolBar functions to modify the style after calling the base class load function. Be advised: if you don't call LoadToolBar, you must call ModifyStyle yourself to modify the style to TBSTYLE_FLAT. Sorry.
Just in case you think this question was selected as a slimy promotional gimmick to make you go out and buy Visual C++ 5.0 to get spiffy new toolbars, I should point out that, in theory, you don't need Visual C++ 5.0. All you need to make flat toolbars work are the new COMCTL32.DLL, which you can download free from Microsoft, and the definition of TBSTYLE_FLAT, which you can write yourself.

 #ifndef TBSTYLE_FLAT
 #define TBSTYLE_FLAT 0x0800
 #endif
Since I'm such a friendly guy, I added these lines to FlatBar.h. So if you haven't yet taken the 5.0 plunge (go ahead, it's fun), all you need is the new COMCTL32.DLL. Like I said, this is theoretical because I didn't personally test it. Figure 3 shows only the CFlatToolBar class, not the FlatBar program that uses it. You can download the full FlatBar app as part of the source code for this article.
Before closing the case on flat toolbars, I must mention one important restriction that we at MSJ unfortunately neglected to mention in Strohm's article. The new common controls in COMCTL32.DLL are not, repeat not, freely distributable in an end user application. I would bet my booty that COMCTL32.DLL, or some version of it, becomes distributable in the near future, but that's just my own personal opinion, not an official Microsoft pronouncement. Meanwhile, if you use CFlatToolbar, it won't work on machines that don't already have IE 3.0 or IE 4.0 installed. You could, however, add some code to check for COMCTL32.DLL, and use the flat look only if the DLL is present. You can also encourage your users to install IE 3.0 or IE 4.0 if they want the flat look. Finally, Microsoft has provided the IEAK, a distribution kit for applications wanting to ship a branded Microsoft Internet Explorer on their Web site. With this kit you can distribute COMCTL32.DLL, along with the rest of Internet Explorer. Because this isn't exactly small in size, this may not be another option.

Q I am attempting to write a simple program with Visual C++ 5.0 and I want to give my toolbars and menus that new 3D look with the sliding bands that Microsoft introduced with Office 97 and Visual Studio 97. Can you give me some insight into how this is done? I'm using Visual C++ 5.0 and MFC.

Ken Perkins and numerous other readers

A Uh oh, here we go again. More UI fashion envy. The toolbars in Office 97 and Visual Developer are proprietary Microsoft window classes not available for general consumption yet; however, the COMCTL32.DLL mentioned in the previous question has something called a coolbar that has many similar features and is what IE 3.0 uses. So now that I showed you how to implement flat toolbars in MFC, the next logical step is to go full hog and implement coolbars. Unfortunately, this is much more difficult and definitely not something for the faint of heart. Lucky for you, I figured it all out myself and wrote some classes that make coolbars a snap, as I will now describe. Hold on to your programming caps.
Figure 4 Coolbar with side-by-side bands
Figure 4 Coolbar with side-by-side bands

Coolbars are those toolbars with the skinny handles at the left end that you can use to size or drag the different bands inside the toolbar (see Figures 4 and 5). Coolbars can also display a bitmap background instead of boring gray. Unlike flat toolbars, which are merely a new style for an old class (ToolbarWindow32), coolbars are an entirely new window class, ReBarWindow32. That's Resizable toolBar. Read Strohm Armstrong's series for a full discussion of coolbars; I will stay focused on MFC issues.
Figure 5 Same coolbar with stacked bands
Figure 5 Same coolbar with stacked bands

The CoolBar program you see in Figures 4 and 5 uses a new class I wrote, CCoolBar, with related classes CRebarInfo, CRebarBandInfo, and CCoolToolBar. CRebarInfo and CRebarBandInfo are programmer-friendly C++ versions of the C structs REBARINFO and REBARBANDINFO, with constructors that initialize the objects to all zeroes before setting the cbSize member appropriately. CCoolBar is an MFC-style wrapper for ReBarWindow32, and CCoolToolBar is a specialized CToolBar to use inside a CCoolBar. See Figure 6 for the executive summary.
Before I take you inside CCoolBar and CCoolToolBar, let me show you how to use them in your app. Figure 7 shows the CMainFrame class from my CoolBar sample app, which is the only class affected. The first thing you'll notice is yet another class, CMyCoolBar, derived from CCoolBar. You can't use CCoolBar directly; you must derive your own class from it and implement the pure virtual function OnCreateBands. Why did I design CCoolBar this way? Because it makes no sense to have a coolbar with no bands, and I have no way of knowing what kinds of bands you want! So I left this part open by making it a pure virtual function. CCoolBar calls OnCreateBands at the right time (when the coolbar is created), so all you have to do is write it.
For the sample CoolBar program, CMyCoolBar::OnCreateBands creates two bands: one for the toolbar and one for the combo box in Figure 4. First, OnCreateBands creates the windows. The combo box is a special class, CMyComboBox, with an OnDropDown handler that generates a list of phony-looking URLs. The toolbar is a CCoolToolBar, not an ordinary CToolBar. I'll discuss CCoolToolBar in a moment; right now you're still looking at OnCreateBands.
After creating the windows that make up the bands, CMyCoolBar::OnCreateBands creates the bands themselves. This is a matter of uttering the proper coolbar voodoo, as described in Strohm's series. You have to fill out a CRebarBandInfo/REBARBANDINFO struct and call CCoolBar::InsertBand. Most of it is pretty straightforward. I load a bitmap into a data member, m_bmBackground, and use the RBBS_FIXEDBMP style to tell the coolbar to paint the bitmap continuously across all bands instead of starting at origin (0,0) again for each band. For the minimum size of the combo box band, I use a hardwired constant; for the toolbar band, I call CToolBar::CalcDynamicLayout. MFC uses CalcDynamicLayout to determine how small it can shrink a dynamic toolbar vertically or horizontally. I use it to get the minimum height and width of the toolbar, which is for all practical purposes the height and width of one button. Once CMyCoolBar::OnCreateBands creates the two bands, it returns zero to indicate success. Whew.
The only thing left to do is create the coolbar. This is done in CMainFrame::OnCreate, which calls m_wndCoolBar.Create. It's just like creating an ordinary CToolBar, except I call it with a zillion—or at least eight—style flags, including RBS_VARHEIGHT, to make my bands movable vertically within my coolbar.
Here's a quick summary of what you have to do to use CCoolBar:
  • Derive CYourOwnCoolBar from CCoolBar and implement OnCreateBands to create the bands you want.
  • If you want a toolbar inside your coolbar (likely), use CCoolToolBar instead of CToolBar.
  • Create the coolbar in your main frame's OnCreate function, as usual.
Pretty easy, huh? For those of you who aren't content to play with your new toys, but have to take them apart to see what's inside, let me now take you quickly through my implementation of CCoolBar. I derived CCoolBar from MFC's CControlBar—the base class for toolbars, status bars, and dialog bars—in order to inherit CControlBar's many useful features like message routing and functions that CFrameWnd uses to calculate its layout. CCoolBar has all the wrapper functions you'd expect—tiny inline functions that convert Windows-style message-speak into C++-style object-speak.

 inline BOOL CCoolBar::GetBarInfo(LPREBARINFO lp)
 {	
 	ASSERT(::IsWindow(m_hWnd));
 	return SendMessage(RB_GETBARINFO, 0, 
                          (LPARAM)lp); 
 }
CCoolBar also has a Create function that makes a one-time call to InitCommonControlsEx with the ICC_COOL_ CLASSES flag to initialize coolbars and an OnCreate handler that calls the previously described virtual function CCoolBar::OnCreateBands so the derived class can create its bands. CCoolBar also overrides two functions inherited from CControlBar, CalcFixedLayout and CalcDynamicLayout, to calculate the size of the coolbar as if it were fixed. I decided to punt on dynamic-sizing coolbars. If you want docking coolbars, you're on your own.
Now we get to the ugly part. The biggest problem I had implementing CCoolBar was crashing into MFC's CControlBar and CToolBar, which have all sorts of hairy code, mostly to do things like docking and dynamic sizing. And you thought the display bug with flat toolbars was bad! I don't have the space or time to describe each problem in detail, so here are the highlights.
CCoolBar::OnPaint calls CWnd::Default to bypass all the bizarre painting in CControlBar::OnPaint. CCoolBar::OnEraseBkgnd bypasses CControlBar::OnEraseBkgnd for a similar reason. If the user sizes a window containing a coolbar, the coolbar will try to maintain the minimum height and width you specified for each band. If the user shrinks the window horizontally, the coolbar may move one band below another to make it fit. The coolbar undoes this action if the user makes the window bigger. Either way, the coolbar's height changes, which affects the layout of the frame; but CFrameWnd has no way of knowing the coolbar changed its height and fails to adjust itself properly, resulting in display bugs. Fortunately, the coolbar sends a RBN_HEIGHTCHANGE message whenever its height changes. CCoolBar handles this message by posting WM_SIZE to the parent frame window, which forces it to recalculate its layout. Problem solved.
All the preceding fixes are for CCoolBar—that is, the coolbar itself. Now I'll turn to the toolbar inside the coolbar. Remember, a coolbar is just a container window that manages sizeable rectangles called bands, which can contain other windows as children. To get buttons inside a coolbar, you must add your toolbar window as a band. But if you use the vanilla MFC CToolBar, you'll run into more display bugs. That's what CCoolToolBar is for—to work around the bugs in CToolBar. There are three overrides, and they all fall into the "bypass the base class" category; CCoolToolBar::OnNcPaint, OnPaint, and OnNcCalcSize all bypass the CToolBar/CControlBar implementations by calling CWnd::Default. The CToolBar/CControlBar implementations of these functions cause display bugs when used inside coolbars.
In my humble opinion, the toolbar code is one of a few major problem areas in MFC. It's contorted, fragile, and almost impossible to modify. Fundamentally, the problem is that the implementation is too tightly knitted into the base classes and spread across umpteen functions, each of which expects the others to behave in just the right way. Change one behavior slightly and the whole thing breaks. No insult intended, but will someone over there in MFC-land please fix this code?
So there you have it. A CoolBar class for MFC. Figure 8 shows the complete CCoolBar and related classes. The sample CoolBar program is available from the source code.
If you decide to use CCoolBar, keep in mind that the same COMCTL32.DLL restriction I mentioned in the previous question applies here: you can't distribute COMCTL32.DLL freely with you app; coolbars will only work on machines that have the latest COMCTL32.DLL. So you must write your app to use old-style toolbars if it doesn't detect the latest COMCTL32.DLL.
I'm sure by the time MFC 6.0 comes out it will have its own CCoolBar class. In the meantime, my homegrown version should be all you need to cure your UI fashion envy, at least until the next fad comes along. (Already, I hear someone asking, "How do I get the button icons to appear in my menus?") As an extra bonus, I even grabbed the background bitmap from IE 3.0—you know, the one with the wavy gray swirls. Gee, I hope they don't sue me.

Have a question about programming in C or C++? Send it to askpd@pobox.com

From the August 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.

© 2016 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies
Microsoft