Toolbars provide a means for users to activate commands and tools contained in an application. To make your application user-friendly, design your toolbar to expose the key functionality of the application. The following topics discuss features to consider when planning a toolbar.
You can use two functions to create a toolbar—
If you create a toolbar using CreateToolbarEx, the function enables
you to specify in pixels the height and width of the toolbar. However, the CreateWindowEx
function does not have parameters for specifying toolbar size. The toolbar
window procedure automatically sets the size and position of the toolbar
window. The height is based on the height of the buttons in the toolbar. The
width is the same as the width of the parent window's client area. To change
the automatic size settings, send a
Also, the toolbar window procedure automatically adjusts the size of the
toolbar whenever it receives a
The toolbar default sizing and positioning behaviors can be turned off by
setting the
When you specify the
When the toolbar receives a
An application that needs to send messages directly to the ToolTip control
can retrieve the handle to the control by using the
There are two ways to specify the images for buttons—by bitmaps or by
image lists. An application must choose which method to use. It cannot use
both methods with the same toolbar control. Note that the
Each button in a toolbar can include a bitmapped image. A toolbar uses an
internal list to store the information that it needs to draw the images. When
you call the
Each image has a zero-based index. The first image added to the internal list has an index of 0, the second image has an index of 1, and so on. TB_ADDBITMAP adds images to the end of the list and returns the index of the first new image that it added. To associate the image with a button, you must send a TB_ADDBUTTONS message and specify the image's index after you add bitmaps to the internal image list.
Microsoft® Windows® assumes that all of a toolbar's bitmapped images are
the same size. You specify the size when you create the toolbar by using CreateToolbarEx.
If you use the CreateWindowEx function to create a toolbar, the size of
the images is set to the default dimensions of 16 by 15 pixels. You can use
the
You can also store button images in a set of
| Normal | Buttons in their default state. |
| Hot | Buttons that are under the pointer or pressed. Hot items are
supported only in toolbar controls that have the |
| Disabled | Buttons that are disabled. |
After the toolbar is destroyed, applications must free any image lists they have created.
Each button can display a string in addition to, or instead of, an image. A
toolbar maintains an internal list that contains all the strings available to
toolbar buttons. You add strings to the internal list by using the
Each string has a zero-based index. The first string added to the internal list of strings has an index of 0, the second string has an index of 1, and so on. TB_ADDSTRING adds strings to the end of the list and returns the index of the first new string. You use a string's index to associate the string with a button.
Using TB_ADDSTRING is not the only way to add strings to a toolbar.
You can display a string in a button by passing a string pointer in the iString
field of the
If you use the CreateToolbarEx function to create a toolbar, you can
add buttons to the toolbar by filling an array of
After a toolbar is created, you can insert buttons by sending a
A button's style determines how the button appears and how it responds to
user input. For instance, the
You can create groups of toolbar buttons that act like radio buttons by
using the
The
Version 5.80 of the common controls introduced some new toolbar button
styles and renamed some of the older styles. All button style flags now begin
with BTNS_XXX instead of TBSTYLE_XXX. For a listing and discussion of the
button styles, see
Each button in a toolbar has a state. The toolbar updates a button's state
to reflect user actions, such as clicking the button. The state indicates
whether the button is currently pressed or not pressed, enabled or disabled,
hidden or visible. Although an application sets a button's initial state when
adding the button to the toolbar, it can change and retrieve the state by
sending
Each button has an application-defined command identifier associated with it. Button identifiers are usually defined in an application header file. For example, a Paste button can be defined as:
#define ID_PASTE 100
When the user selects a button, the toolbar sends the parent window a
A toolbar keeps track of its buttons by assigning each button a position index. The index is zero-based; that is, the leftmost button has an index of 0, the next button to the right has an index of 1, and so on. An application must specify the index of a button when sending messages to retrieve information about the button or to set the button's attributes.
A toolbar updates the position indexes as buttons are inserted and removed.
An application can retrieve the current position index of a button by using
the
All buttons in a toolbar are the same size. The CreateToolbarEx
function requires you to set the initial size of the buttons when you create
the toolbar. When you use the
When you add a string that is longer than any string currently in the toolbar, the toolbar automatically resets the width of its buttons. The width is set to accommodate the longest string in the toolbar.
A toolbar has built-in customization features that you can make available
to the user by giving the toolbar the
As part of the customization process, applications often need to save and
restore a toolbar's state. For instance, many applications store the toolbar
state before the user begins customizing the toolbar in case the user later
wants to restore the toolbar to its original state. The toolbar control does
not automatically keep a record of its precustomization state. Your
application must save the toolbar state in order to restore it. For more
information, see
Toolbar controls created with the TBSTYLE_FLAT style support hot-tracking
by default. Hot-tracking means that when the pointer moves over an item, it is
highlighted but not selected. When the user moves the pointer over a toolbar
button, the button's appearance changes. You can use other window styles in
combination with TBSTYLE_FLAT to produce toolbars that enable hot-tracking but
have a different appearance from a flat toolbar. For more information, see
Toolbar frames serve as containers for toolbars and other child windows. You implement toolbar frames by creating a rebar control and adding one or more controls to it. A rebar control hosts one or more bands. Each band contains only one child window.
Rebar controls are separate controls but they are often used to host
toolbar controls. Using toolbar and rebar controls together enables you to
write applications that are more flexible. For example, toolbars can be moved,
repositioned, minimized, and maximized within the rebar control. For more
information about programming rebar controls, see
This section contains examples that demonstrate how to create and use toolbar controls in your applications.
You can use two functions to create a toolbar—
The following CreateAToolBar sample function uses
// CreateAToolBar creates a toolbar and adds a set of buttons to it.
// The function returns the handle to the toolbar if successful,
// or NULL otherwise.
// hwndParent is the handle to the toolbar's parent window.
HWND CreateAToolBar(HWND hwndParent)
{
HWND hwndTB;
TBADDBITMAP tbab;
TBBUTTON tbb[3];
char szBuf[16];
int iCut, iCopy, iPaste;
INITCOMMONCONTROLSEX icex;
HRESULT hr;
size_t cch;
// Ensure that the common control DLL is loaded.
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&icex);
// Create a toolbar.
hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL,
WS_CHILD | CCS_ADJUSTABLE, 0, 0, 0, 0, hwndParent,
(HMENU) ID_TOOLBAR, g_hinst, NULL);
// Send the TB_BUTTONSTRUCTSIZE message, which is required for
// backward compatibility.
SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
// Add the button strings to the toolbar's internal string list.
LoadString(g_hinst, IDS_CUT, szBuf, MAX_LEN-1);
//Save room for second null terminator.
hr = StringCchLength(szBuf, MAX_LEN, &cch);
if(SUCCEEDED(hr))
{
szBuf[cch + 2] = 0; //Double-null terminate.
}
else
{
// TODO: Write error handler.
}
iCut = SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM) (LPSTR) szBuf);
LoadString(g_hinst, IDS_COPY, szBuf, MAX_LEN-1);
//Save room for second null terminator.
hr = StringCchLength(szBuf, MAX_LEN, &cch);
if(SUCCEEDED(hr))
{
szBuf[cch + 2] = 0; //Double-null terminate.
}
else
{
// TODO: Write error handler.
}
iCopy = SendMessage(hwndTB, TB_ADDSTRING, (WPARAM) 0,
(LPARAM) (LPSTR) szBuf);
LoadString(g_hinst, IDS_PASTE, szBuf, MAX_LEN-1);
//Save room for second null terminator.
hr = StringCchLength(szBuf, MAX_LEN, &cch);
if(SUCCEEDED(hr))
{
szBuf[cch + 2] = 0; //Double-null terminate.
}
else
{
// TODO: Write error handler.
}
iPaste = SendMessage(hwndTB, TB_ADDSTRING, (WPARAM) 0,
(LPARAM) (LPSTR) szBuf);
// Fill the TBBUTTON array with button information, and add the
// buttons to the toolbar. The buttons on this toolbar have text
// but do not have bitmap images.
tbb[0].iBitmap = I_IMAGENONE;
tbb[0].idCommand = IDS_CUT;
tbb[0].fsState = TBSTATE_ENABLED;
tbb[0].fsStyle = BTNS_BUTTON;
tbb[0].dwData = 0;
tbb[0].iString = iCut;
tbb[1].iBitmap = I_IMAGENONE;
tbb[1].idCommand = IDS_COPY;
tbb[1].fsState = TBSTATE_ENABLED;
tbb[1].fsStyle = BTNS_BUTTON;
tbb[1].dwData = 0;
tbb[1].iString = iCopy;
tbb[2].iBitmap = I_IMAGENONE;
tbb[2].idCommand = IDS_PASTE;
tbb[2].fsState = TBSTATE_ENABLED;
tbb[2].fsStyle = BTNS_BUTTON;
tbb[2].dwData = 0;
tbb[2].iString = iPaste;
SendMessage(hwndTB, TB_ADDBUTTONS, (WPARAM) NUM_BUTTONS,
(LPARAM) (LPTBBUTTON) &tbb);
SendMessage(hwndTB, TB_AUTOSIZE, 0, 0);
ShowWindow(hwndTB, SW_SHOW);
return hwndTB;
}
You are not required to make an internal list of strings to add text to a
button. There are alternative methods. One alternative is to pass a string
pointer to the iString member of a
tbb[0].iBitmap = -1; tbb[0].idCommand = IDS_CUT; tbb[0].fsState = TBSTATE_ENABLED; tbb[0].fsStyle = TBSTYLE_BUTTON; tbb[0].dwData = 0; tbb[0].iString = (int) pszBuf; // Text is in a buffer.
Another way to assign text to a button is with the
char szBuf[8] = "CHANGE";
LPSTR pszBuf;
TBBUTTONINFO tbi;
LPTBBUTTONINFO lptbbi;
// Other code here.
// Initialize pszBuf and lptbbi.
// These are the only fields of the TBBUTTONINFO structure that
// need to be changed to modify text.
tbi.dwMask = TBIF_TEXT;
tbi.cbSize = sizeof (TBBUTTONINFO);
tbi.pszText = pszBuf;
tbi.cchText = sizeof (szBuf);
SendMessage(hwndTB, TB_SETBUTTONINFO, IDS_PASTE, (LPARAM)
(LPTBBUTTONINFO)lptbbi);
This example uses CreateToolbarEx to create a toolbar and adds
standard bitmaps to the toolbar. The standard toolbar bitmaps are built into
Comctl32.dll. You do not have to use the standard bitmaps; you can use your
own custom bitmaps. To add a bitmap to an application, use the
Show Example
// Toolbar buttons used to create the first 4 buttons.
TBBUTTON tbButtonsCreate [ ] =
{
{STD_FILENEW, IDM_NEW, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{STD_FILEOPEN, IDM_OPEN, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{STD_FILESAVE, IDM_SAVE, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{0, 0, TBSTATE_ENABLED, BTNS_SEP,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
};
// Toolbar buttons to add.
TBBUTTON tbButtonsAdd [ ] =
{
{VIEW_LARGEICONS, IDM_LARGEICON, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{VIEW_SMALLICONS, IDM_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, #if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{VIEW_LIST, IDM_LISTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, #if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{VIEW_DETAILS, IDM_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, #if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
};
// Also the code example uses a MACRO defined in the header file as
// follows: #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
// CreateAToolBar2 creates a toolbar and adds bitmaps to it.
// The function returns the handle to the toolbar if successful,
// or NULL otherwise.
// The function uses HINST_COMMCTRL as the HINSTANCE and
// IDB_STD_SMALL_COLOR as the bitmap identifier.
// hwndParent is the handle to the toolbar's parent window.
HWND CreateAToolBar2(HWND hWndParent)
{
HWND hWndToolbar;
TBADDBITMAP tb;
int index, stdidx;
// Create a toolbar with three standard file bitmaps and one
// separator.
// There are 15 items in IDB_STD_SMALL_COLOR. However, because this is a
// standard system-defined bitmap, the parameter that specifies the number
// of button images in the bitmap (nBitmaps) is ignored, so it is set
// to 0.
hWndToolbar = CreateToolbarEx (hWndParent,
WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS,
ID_TOOLBAR, 0, HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
tbButtonsCreate, ARRAYSIZE(tbButtonsCreate), 0, 0, 100, 30, sizeof (TBBUTTON));
// Add four view bitmaps. The view bitmaps are not in the same
// file as the standard bitmaps; therefore, you must change the
// resource identifier from IDB_STD_SMALL_COLOR to
// IDB_VIEW_SMALL_COLOR.
tb.hInst = HINST_COMMCTRL;
tb.nID = IDB_VIEW_SMALL_COLOR;
// There are 12 items in IDB_VIEW_SMALL_COLOR. However, because this is a standard
// system-defined bitmap, wParam (nButtons) is ignored.
stdidx = SendMessage (hWndToolbar, TB_ADDBITMAP, 0, (LPARAM)&tb);
// Update the indexes to the view bitmaps.
for (index = 0; index < 4; index++)
tbButtonsAdd[index].iBitmap += stdidx;
// Add the view buttons.
SendMessage (hWndToolbar, TB_ADDBUTTONS, ARRAYSIZE(tbButtonsAdd), (LPARAM) &tbButtonsAdd[0]);
return hWndToolbar;
}
Toolbar controls support a transparent look that allows the client area
under the toolbar to show through. There are two kinds of transparent
toolbars, ones with flat buttons and ones with three-dimensional buttons. If
you want your application to match the Microsoft® Windows® interface, use
the flat transparent style toolbar. You create a flat transparent toolbar by
including the

To create a transparent toolbar, all you need to do is add TBSTYLE_FLAT or
TBSTYLE_TRANSPARENT to the window style parameter of
hWndToolbar = CreateToolbarEx (hWndParent,
WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
ID_TOOLBAR, 11, (HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
(LPCTBBUTTON)&tbButtons, 4, 0, 0, 100, 30, sizeof (TBBUTTON));
To produce a transparent toolbar with raised buttons, change TBSTYLE_FLAT to TBSTYLE_TRANSPARENT.
Toolbar buttons enable you to display both text and bitmaps. The buttons on
a toolbar created with the

The following code snippet creates a toolbar with the TBSTYLE_LIST style.
hWndToolbar = CreateToolbarEx (hWndParent,
WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS | TBSTYLE_LIST,
ID_TOOLBAR, 11, (HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
(LPCTBBUTTON)&tbButtons, 4, 0, 0, 100, 30, sizeof (TBBUTTON));
To create a flat list-style toolbar, use the following:
hWndToolbar = CreateToolbarEx (hWndParent,
WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT |
TBSTYLE_LIST, ID_TOOLBAR, 11, (HINSTANCE)HINST_COMMCTRL,
IDB_STD_SMALL_COLOR, (LPCTBBUTTON)&tbButtons, 4, 0, 0, 100,
30, sizeof (TBBUTTON));
When you create a list-style toolbar, you should provide text and bitmaps
to avoid blank spaces on the button. For information about adding text and
bitmaps to a button, see Assigning
text to a toolbar button and
Applications that need to customize the text displayed in toolbar ToolTips
must respond to the
A toolbar that has the
The following example specifies an instance handle and a resource identifier to handle the TTN_GETDISPINFO ToolTip notification.
Show Example
case WM_NOTIFY:
switch (((LPNMHDR) lParam)->code)
{
case TTN_GETDISPINFO:
{
LPTOOLTIPTEXT lpttt;
lpttt = (LPTOOLTIPTEXT) lParam;
lpttt->hinst = g_hinst;
// Specify the resource identifier of the descriptive
// text for the given button.
idButton = lpttt->hdr.idFrom;
switch (idButton)
{
case IDM_CUT:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_CUT);
break;
case IDM_COPY:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_COPY);
break;
case IDM_PASTE:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_PASTE);
break;
}
break;
}
.
. // Process other notifications here.
.
default:
break;
}
If an application specifies labels in the iString member of the
associated TBBUTTON structure, the toolbar control automatically uses
that string as the ToolTip. You must send a
You can also provide ToolTips by using the
A drop-down button is designed to present users with a list of options. You
create this style button by specifying the

When the user clicks a toolbar button that uses the BTNS_DROPDOWN style,
the toolbar control sends its parent a
An application can support a drop-down button in a toolbar control, as illustrated in the following DoNotify application-defined function:
BOOL DoNotify(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#define lpnm ((LPNMHDR)lParam)
#define lpnmTB ((LPNMTOOLBAR)lParam)
RECT rc;
TPMPARAMS tpm;
HMENU hPopupMenu = NULL;
HMENU hMenuLoaded;
BOOL bRet = FALSE;
switch(lpnm->code){
case TBN_DROPDOWN:
SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT,
(WPARAM)lpnmTB->iItem, (LPARAM)&rc);
MapWindowPoints(lpnmTB->hdr.hwndFrom,
HWND_DESKTOP, (LPPOINT)&rc, 2);
tpm.cbSize = sizeof(TPMPARAMS);
tpm.rcExclude = rc;
hMenuLoaded = LoadMenu(g_hinst, MAKEINTRESOURCE(IDR_POPUP));
hPopupMenu = GetSubMenu(LoadMenu(g_hinst,
MAKEINTRESOURCE(IDR_POPUP)),0);
TrackPopupMenuEx(hPopupMenu,
TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,
rc.left, rc.bottom, g_hwndMain, &tpm);
DestroyMenu(hMenuLoaded);
return (FALSE);
}
return FALSE;
}
Most Windows-based applications use toolbar controls to provide their users with convenient access to various tools. However, static toolbars have some shortcomings, such as too little space to effectively display all the available tools.
The solution to this problem is to make your application's toolbars customizable. Users can then move, add, and delete tools to select only the ones they need and organize them in whatever way they find convenient.
To enable customization, include the
You can implement either or both, depending on the needs of the application. Neither of these two approaches to customization provides a built-in mechanism, such as a Cancel or Undo button, to return the toolbar to its former state. You must explicitly use the toolbar control API to store the toolbar's precustomization state. If necessary, you can later use this stored information to restore the toolbar to its original state.
This topic discusses how to enable toolbar customization with the customization dialog box and with drag and drop.
The customization dialog box is provided by the toolbar control to give
users a simple way to add, move, or delete tools. Users can launch it by
double-clicking the toolbar. Applications can launch the customization dialog
box by sending the toolbar control a

The tools in the right-hand list box are those currently on the toolbar. Initially, this list will contain the tools that you specify when you create the toolbar. The left-hand list box contains the tools that are available to add to the toolbar. Your application is responsible for populating that list and keeping track of what tools are currently on the toolbar.
The toolbar control notifies your application that it is launching a
customization dialog box by sending its parent window a
The toolbar control then collects the information it needs to initialize the dialog box by sending three series of notifications in the following order:
The dialog box is then displayed, and the user can begin to customize the toolbar.
Once the dialog box is displayed, your application can receive a variety of notifications, depending on the users' actions:
After the dialog box is destroyed, your application will receive a
The following code example demonstrates how to handle notifications to enable toolbar customization.
Show Example
LRESULT MsgNotify(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
static UINT i=0;
LPNMHDR lpnmhdr;
lpnmhdr = (LPNMHDR)lparam;
// The following code allows the toolbar to be customized.
// If you return FALSE the Customize Toolbar dialog flashes
// and goes away.
if (lpnmhdr->code == TBN_QUERYINSERT || lpnmhdr->code ==
TBN_QUERYDELETE)
{
return TRUE;
}
if (lpnmhdr->code == TBN_GETBUTTONINFO)
{
LPTBNOTIFY lpTbNotify = (LPTBNOTIFY)lparam;
char szBuffer [20];
// 20 = total number of buttons.
// tbButton and tbButtonNew send information about
// the other 12 buttons in tbButtonNew.
if (lpTbNotify->iItem < 12)
{
lpTbNotify->tbButton = tbButtonNew[lpTbNotify->iItem];
LoadString(hInst,
4000+ lpTbNotify->iItem,
szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]));
hr = StringCchCopy
(lpTbNotify->pszText,
sizeof(lpTbNotify->pszText)
/sizeof(lpTbNotify->pszText[0]),
szBuffer );
if(SUCEEDED(hr))
{
lpTbNotify->cchText =
sizeof(szBuffer)/sizeof(szBuffer[0]);
}
else
{
TODO: Write error handler.
}
return TRUE;
}
else
return 0;
}
return 0;
}
Users can also rearrange the buttons on a toolbar by pressing the SHIFT key and dragging the button to another location. The drag-and-drop process is handled automatically by the toolbar control. It displays a ghost image of the button as it is dragged, and rearranges the toolbar after it is dropped. Users cannot add buttons in this way, but they can delete a button by dropping it off the toolbar.
Although the toolbar control normally does this operation automatically, it also sends your application two notifications: TBN_QUERYDELETE and TBN_QUERYINSERT. To control the drag-and-drop process, handle these notifications as follows:
If the user attempts to drag a button without also pressing the SHIFT key,
the toolbar control will not handle the drag-and-drop operation. However, it
will send your application a
In the process of customizing a toolbar, your application might need to
save information so that you can restore the toolbar to its original state. To
initiate saving or restoring a toolbar state, send the toolbar control a
Toolbar states are saved in a data stream that consists of blocks of Shell-defined data alternating with blocks of application-defined data. One data block of each type is stored for each button, along with an optional block of global data that applications can place at the beginning of the stream. During the save process, your TBN_SAVE handler adds the application-defined blocks to the data stream. During the restore process, the TBN_RESTORE handler reads each block and gives the Shell the information it needs to reconstruct the toolbar.
The first TBN_SAVE notification is sent at the beginning of the save
process. Before any buttons are saved, the members of the
| Member | Setting |
|---|---|
| iItem | -1 |
| cbData | Amount of memory needed for Shell-defined data. |
| cButtons | Number of buttons. |
| pData | Calculated amount of memory needed for application-defined data. Typically, you include some global data, plus data for each button. Add that value to cbData and allocate enough memory to pData to hold it all. |
| pCurrent | First unused byte in the data stream. If you do not require global toolbar information, set pCurrent = pData so that it points to the start of the data stream. If you do require global toolbar information, store it at pData, then set pCurrent to the beginning of the unused portion of the data stream before returning. |
If you want to add some global toolbar information, put it at the start of the data stream. Advance pCurrent to the end of the global data so that it points to the beginning of the unused portion of the data stream, and return.
After you return, the Shell starts saving button information. It adds the Shell-defined data for the first button at pCurrent and then advances pCurrent to the start of the unused portion.
After each button is saved, a TBN_SAVE notification is sent and NMTBSAVE is returned with these members set as follows.
| Member | Setting |
|---|---|
| iItem | Zero-based index of the button number. |
| pCurrent | Pointer to the first unused byte in the data stream. If you want to store additional information about the button, store it at the location pointed to by pCurrent and update pCurrent to point to the first unused portion of the data stream after that. |
| TBBUTTON |
When you receive the notification, you should extract any button-specific information you need fromTBBUTTON. Remember that when you add a button, you can use the dwData member of TBBUTTON to hold application-specific data. Load your data into the data stream at pCurrent. Advance pCurrent to the end of your data, again pointing to the beginning of the unused portion of the stream, and return.
The Shell then goes to the next button, adds its information to pData, advances pCurrent, loads TBBUTTON, and sends another NMTBSAVE notification. This process continues until all buttons are saved.
The restore process basically reverses the save process. At the beginning,
your application will receive a
Your notification handler should extract the global information placed at the beginning of pData during the save, and advance pCurrent to the start of the first block of Shell-defined data. Set cBytesPerRecord to the size of the data blocks you used to save the button data. Set cButtons to the number of buttons, and return.
The next NMTBRESTORE is for the first button. The pCurrent member points to the start of your first block of button data, and iItem is to the button index. Extract that data and advance pCurrent. Load the data into TBBUTTON, and return. To omit a button from the restored toolbar, set the idCommand member of TBBUTTON to zero. The Shell will repeat the process for the remaining buttons.
In addition to NMTBSAVE and NMTBRESTORE messages, you can also use messages such as TBN_RESET to save and restore a toolbar. The following code snippet saves a toolbar before it is customized and restores it if the application receives a TBN_RESET message.
int i;
LPNMHDR lpnmhdr;
static int nResetCount;
static LPTBBUTTON lpSaveButtons;
LPARAM lParam;
switch( lpnmhdr->code)
{
case TBN_BEGINADJUST: // Start customizing the toolbar.
{
LPTBNOTIFY lpTB = (LPTBNOTIFY)lparam;
// Allocate memory to store the button information.
nResetCount = SendMessage(lpTB->hdr.hwndFrom,
TB_BUTTONCOUNT, 0, 0);
lpSaveButtons = (LPTBBUTTON)GlobalAlloc(GPTR,
sizeof(TBBUTTON) * nResetCount);
// Save the current configuration so if the user presses
// reset, the original toolbar can be restored.
for(i = 0; i < nResetCount; i++)
{
SendMessage(lpTB->hdr.hwndFrom, TB_GETBUTTON, i,
(LPARAM)(lpSaveButtons + i));
}
}
return TRUE;
case TBN_RESET:
{
LPTBNOTIFY lpTB = (LPTBNOTIFY)lparam;
int nCount, i;
// Remove all of the existing buttons starting with the
// last and working down.
nCount = SendMessage(lpTB->hdr.hwndFrom, TB_BUTTONCOUNT, 0,
0);
for(i = nCount - 1; i >= 0; i--)
{
SendMessage(lpTB->hdr.hwndFrom, TB_DELETEBUTTON, i, 0);
}
// Restore the buttons that were saved.
SendMessage(lpTB->hdr.hwndFrom, TB_ADDBUTTONS,
(WPARAM)nResetCount, (LPARAM)lpSaveButtons);
}
return TRUE;
case TBN_ENDADJUST:
// Free the memory allocated during TBN_BEGINADJUST
GlobalFree((HGLOBAL)lpSaveButtons);
return TRUE;
}
Toolbars support only buttons; therefore, if your application requires a different kind of control, you must create a child window. The following illustration shows a toolbar with an embedded edit control.

Any type of window can be placed on a toolbar. The following sample code
adds an edit control as a child of the toolbar control window. Because the
toolbar is created and then the edit control added, you must provide space for
the edit control. One way to do this is to add a separator as a placeholder in
the toolbar. For example, to add an edit control to the second code sample in
the Creating Toolbars section
of this article, you first add a separator to tbButtonsCreate, a customized
The following application-defined function creates the toolbar, and adds the buttons and the edit control.
HWND CreateAToolBar(HWND hWndParent)
{
HWND hWndToolbar;
HWND hWndEdit;
TBADDBITMAP tb;
int index, stdidx;
int cx_edit, CX_SEPARATOR = 250; // Width of edit control.
int cy_edit = 35; // Height of edit control.
// The code example uses a MACRO defined in the header file as
// follows: #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
// Toolbar buttons
TBBUTTON tbButtonsCreate [ ] =
{
// The separator is set to the width of the edit control.
{CX_SEPARATOR, 0, TBSTATE_ENABLED, BTNS_SEP,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0, -1},
{STD_FILENEW, IDM_NEW, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{STD_FILEOPEN, IDM_OPEN, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{STD_FILESAVE, IDM_SAVE, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{0, 0, TBSTATE_ENABLED, BTNS_SEP,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
};
TBBUTTON tbButtonsAdd [ ] =
{
{VIEW_LARGEICONS, IDM_LARGEICON, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{VIEW_SMALLICONS, IDM_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{VIEW_LIST, IDM_LISTVIEW, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
{VIEW_DETAILS, IDM_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON,
#if defined(_WIN32) | defined(_WIN64)
{0},
#endif
0L, 0},
};
// Create a toolbar containing separators and three buttons.
// There are 15 items in IDB_STD_SMALL_COLOR. However, because this is a
// standard system-defined bitmap, the parameter that specifies the number
// of button images in the bitmap (nBitmaps) is ignored, so it is set
// to 0.
hWndToolbar = CreateToolbarEx (hWndParent,
WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS,
ID_TOOLBAR, 0, HINST_COMMCTRL,
IDB_STD_SMALL_COLOR, tbButtonsCreate, ARRAYSIZE(tbButtonsCreate), 0, 0, 100,
30, sizeof (TBBUTTON));
// Add four view bitmaps. The view bitmaps are not in the same
// file as the standard bitmaps; therefore, you must change the
// resource identifier to IDB_VIEW_SMALL_COLOR.
tb.hInst = HINST_COMMCTRL;
tb.nID = IDB_VIEW_SMALL_COLOR;
// There are 12 items in IDB_VIEW_SMALL_COLOR. However, because this is a standard
// system-defined bitmap, wParam (nButtons) is ignored.
stdidx = SendMessage (hWndToolbar, TB_ADDBITMAP, 0,
(LPARAM)&tb);
// Update the indexes to the view bitmaps.
for (index = 0; index < 4; index++)
tbButtonsAdd[index].iBitmap += stdidx;
// Add the view buttons.
SendMessage (hWndToolbar, TB_ADDBUTTONS, ARRAYSIZE(tbButtonsAdd), (LPARAM)
&tbButtonsAdd[0]);
// Create the edit control child window.
hWndEdit = CreateWindowEx(0L, "Edit", NULL, WS_CHILD | WS_BORDER
| WS_VISIBLE | ES_LEFT | ES_AUTOVSCROLL | ES_MULTILINE,
0, 0, cx_edit, cy_edit, hWndToolbar, (HMENU) IDM_EDIT, hInst, 0 );
// Return the toolbar with the embedded edit control.
return hWndToolbar;
}
The sample hard-codes the dimensions of the child window; however, to make a more robust application, determine the size of the toolbar and make the edit control window to fit.
You might want the edit control notifications to go to another window, such as the toolbar's parent. To do this, create the edit control as a child of the toolbar's parent window. Then change the parent of the edit control to the toolbar. Notifications go to the original parent; therefore, the edit control messages go to the parent of the toolbar and yet the edit window resides in the toolbar window. The following code example demonstrates this.
// Create the edit control. Notice that hWndParent, parent of // the toolbar, is used as the parent of the edit control. hWndEdit = CreateWindowEx(0L, "Edit", NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | ES_LEFT | ES_AUTOVSCROLL | ES_MULTILINE, 0, 0, cx_edit, cy_edit, hWndParent, (HMENU) IDM_EDIT, hInst, 0 ); // Set the toolbar window as the parent of the edit control // window. You must set the toolbar as the parent of the edit // control for it to appear embedded in the toolbar. SetParent (hWndEdit, hWndToolbar);
When a mouse pointer hovers over an item, the item becomes hot. If
hot-tracking is enabled, the hot item is selected and the item under the mouse
pointer is highlighted. Hot-tracking is supported by common controls, such as
the toolbar, list view, tab, and header. Some controls have window styles that
provide hot-tracking by default; others require that a specific hot-tracking
window style be used. A toolbar created with the TBSTYLE_FLAT style supports
hot-tracking by default. You can use TBSTYLE_FLAT in combination with other
window styles to produce toolbars that enable hot-tracking. Hot-tracking
requires that you create image lists; therefore, you cannot use
When the mouse hovers over a toolbar button, the button is outlined to highlight it. The following illustration shows a flat or transparent toolbar with hot-tracking enabled.

If you want a toolbar button bitmap to change when the state of the control
changes, store the different images in
// Create the image list, himlHot.
himlHot = ImageList_Create(MYICON_CX,MYICON_CY,ILC_COLOR8,0,4);
// Load a bitmap from a resource file and adds the images to the
// image list. The bitmap contains four images.
hBitmapHot = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_HOT));
ImageList_Add(himlHot, hBitmapHot, NULL);
// Set the image list.
SendMessage(hwndTB, TB_SETHOTIMAGELIST, 0, (LPARAM)himlHot);
// Loop to fill the array of TBBUTTON structures.
for(i=0;i<MAX_BUTTONS;i++)
{
tbArray[i].iBitmap = i; // Bitmap from image list.
tbArray[i].idCommand = IDM_BUTTONSTART + i;
tbArray[i].fsState = TBSTATE_ENABLED;
tbArray[i].fsStyle = BTNS_DROPDOWN;
tbArray[i].dwData = 0;
tbArray[i].iString = i;
}
// Delete the loaded bitmap.
DeleteObject(hBitmapHot);