Fun with Common Controls Bitmaps and Toolbars






4.45/5 (9 votes)
A WTL mix-in class for creating toolbar controls using common controls bitmaps instead of local resources.
Introduction
I've been lurking around The Code Project for years now, but this is my first
contribution. It's a mix-in class to help a CFrameWindowImpl
derived class build a toolbar using the standard WIN32 common controls bitmaps.
This class can be used to avoid weighing that lean-and-mean WTL app down with
bitmap resources for such commonplace toolbar buttons as print, save, etc. While
this may seem like an awful lot of trouble for nothing, I'm a WTL newbie and I
wanted to see if I could make my WTL app a little bit leaner. Also, for a
variety of other reasons, I like using system resources and controls instead of
custom objects. For instance, if a resource is updated when a new version of
Windows comes out, an application that inherits from the system automatically
inherits the new look, whereas an application that defines its own resource must
often be rebuilt just to avoid looking dated.
Background
The common controls bitmap constants and the indices to the individual images within those bitmaps are documented in the Platform SDK in various locations depending on the SDK version. Two locations I know of are:
- User Interface Services/Shell and Common Controls/Common Controls/Toolbar Controls/Toolbar Control Reference/Toolbar Standard Button Image Index Values
- User Interface Services/Windows Controls/Individual Control Information/Toolbar Controls/Toolbar Controls Reference/Constants/Toolbar Standard Button Image Index Values
In some cases, these constants are incorrectly documented. They're defined in
the Platform SDK's <commctrl.h> file, which is also where the
TBBUTTON
structure type is defined:
typedef struct _TBBUTTON { int iBitmap; int idCommand; BYTE fsState; BYTE fsStyle; #ifdef _WIN64 BYTE bReserved[6]; // padding for alignment #elif defined(_WIN32) BYTE bReserved[2]; // padding for alignment #endif DWORD_PTR dwData; INT_PTR iString; } TBBUTTON, NEAR* PTBBUTTON, *LPTBBUTTON;
It includes some undocumented padding which varies in size depending on the
target platform. To simplify TBBUTTON
array initialization
statements, I define the TBBPADDING
macro in
SysToolbarCtrl.h, and macros for buttons corresponding to the toolbar
icons.
In order to make the demo a little more interesting than a simple frame window with a do-nothing toolbar, I've stolen (well, OK, "borrowed" may be a gentler way of putting it) some other peoples' ideas:
- Leon Finker's IShellBrowser article
- Ed Gadziemski's Pixview sample application, which sadly doesn't seem to be
available on Code Project anymore. I used his
CPix
class, which usesIPicture
to load and render image files. I guess there are plenty of other examples of how to useIPicture
.
Using the code
There are four simple steps to using this code:
- This probably doesn't need to be said, but I'll say it anyway: include the
header file SysToolbarCtrl.h in your mainframe class precompiler
directives:
#include "SysToolbarCtrl.h"
- Include
CFrameWindowImpl
andCSysToolbarCtrl
in your mainframe's inheritance list:class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CSysToolbarCtrl<CMainFrame>, ...
- Instead of calling
CreateSimpleToolBarCtrl
in your mainframe class'OnCreate
method (or whatever technique you normally use to create your toolbar), declare and initialize aTBBUTTON
array, then callCreateSysToolbarCtrl
as follows:// init toolbar button array, including separators, if any. TBBUTTON tbb[] = { // File|New STBB_STD_FILENEW, // File|Save STBB_STD_FILESAVE, // separator STBB_SEPARATOR, // View|Parent folder STBB_VIEW_PARENTFOLDER(ID_VIEW_PARENTFOLDER), // separator STBB_SEPARATOR, // Help|About STBB(STD_HELP, ID_APP_ABOUT, IDB_STD_SMALL_COLOR) }; // create the toolbar control HWND hWndToolBar = CreateSysToolbarCtrl(m_hWnd, tbb, sizeof(tbb)/sizeof(TBBUTTON), &m_CmdBar);
The code uses some macros I defined to specify the toolbar buttons. The macros come in three basic forms, which all use
STBB
(System Tool Bar Button) as a prefix:STBB_IMG_ID
, whereIMG_ID
is the button's image identifier as defined in <commctrl.h>. This form assigns a default command ID to the button using the command IDs defined in <atlres.h>;STBB_IMG_ID(CMD_ID)
, whereIMG_ID
is as described above, andCMD_ID
is the command ID that should be associated with the toolbar button. This is used when there is no standard command ID in the <atlres.h> file that corresponds to the button image, as in the case ofVIEW_PARENTFOLDER
;STBB(IMG_ID, CMD_ID, BITMAP_ID)
, whereIMG_ID
andCMD_ID
are as described previously, andBITMAP_ID
is the bitmap ID defined in <commctrl.h> that contains the button image identified byIMG_ID
. This is the one to use when you want to override the default command ID for a particular toolbar button, as I do in the demo for Help|About.
Use your favorite resource editor to define your menus as you normally would. You can include the command IDs defined by your resource editor in the system toolbar. If <atlres.h> pre-defines any command IDs you could use in your menus, those can be used as well. Needless to say, the specified command ID should have an associated handler somewhere in the application's message map, and may have a corresponding menu item as well. In the demo, I've only provided a handler for two of the buttons (View|Parent folder, and Help|About) but it should be enough to give you a general idea of how it all works.
You can also include a pointer to a
CCommandBarCtrl
as a parameter, andCreateSysToolbarCtrl
will put the bitmaps on the appropriate menu items. If you aren't using a command bar, passingNULL
(the default value) will causeCreateSysToolbarCtrl
to ignore this parameter. - Last, but not least, don't forget to remove any references to the toolbar
bitmap you no longer want, from your resource script (*.rc file). You
might want to do this manually using a bare-bones text editor instead of Visual
C++. Simply commenting out the unwanted line should work:
/////////////////////////////////////////////////////////////////////// // // Bitmap // // IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"
Now if you try to open the toolbar in the Visual C++ IDE, it will squawk at you and ask you if you want to create a bitmap for the toolbar. JUST SAY NO!
Points of Interest
- The
STD_HELP
image is the arrow-question-mark traditionally used for context help. Where's the classic yellow question mark? - There is no image for
VIEW_VIEWMENU
on any system I've looked at, yet it's in <commctrl.h>. What gives? - For the enterprising reader, there are other system bitmaps available to make hot/cold image toolbars. Of course, the code would have to be modified somewhat, and that's beyond the scope of this article.
- Yes, I know, I might be cheating when my
IShellBrowser
implementation gets a selected item's PIDL from theIShellView
window. That's because I don't know the "right" way to do it. - I found a way to synch a tree control with the shell folder view, but it's an undocumented hack and I didn't include it in this article. It's really just a brute-force method, but if anybody's interested, email me and I'll send you the code.