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];
#elif defined(_WIN32)
BYTE bReserved[2];
#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
uses IPicture
to load and render image files. I guess there are
plenty of other examples of how to use IPicture
.
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
and CSysToolbarCtrl
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 a TBBUTTON
array, then
call CreateSysToolbarCtrl
as follows:
TBBUTTON tbb[] =
{
STBB_STD_FILENEW,
STBB_STD_FILESAVE,
STBB_SEPARATOR,
STBB_VIEW_PARENTFOLDER(ID_VIEW_PARENTFOLDER),
STBB_SEPARATOR,
STBB(STD_HELP, ID_APP_ABOUT, IDB_STD_SMALL_COLOR)
};
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
, where IMG_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)
, where IMG_ID
is as described
above, and CMD_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 of VIEW_PARENTFOLDER
;
STBB(IMG_ID, CMD_ID, BITMAP_ID)
, where IMG_ID
and
CMD_ID
are as described previously, and BITMAP_ID
is
the bitmap ID defined in <commctrl.h> that contains the button
image identified by IMG_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, and CreateSysToolbarCtrl
will put the bitmaps on the
appropriate menu items. If you aren't using a command bar, passing
NULL
(the default value) will cause
CreateSysToolbarCtrl
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:
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 the IShellView
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.