This topic contains implementation details and example code for using toolbar controls in your applications.
Creating Toolbars
The CreateToolbarEx function creates a toolbar and adds an initial set of buttons to it. This function does not support newer features of toolbars and is deprecated. Instead, use CreateWindowEx, specifying the TOOLBARCLASSNAME window class, to create a toolbar that initially contains no buttons. Add buttons to the toolbar by using the TB_ADDBUTTONS or TB_INSERTBUTTON message. You must send the TB_AUTOSIZE message after all the items and strings have been inserted into the control to cause the toolbar to recalculate its size based on its content.
The following example code creates the toolbar shown in the illustration, using standard system icons. The Save button is initially disabled.
.png)
HWND CreateSimpleToolbar(HWND hWndParent)
{
// Define some constants.
const int ImageListID = 0;
const int numButtons = 3;
const DWORD buttonStyles = BTNS_AUTOSIZE;
const int bitmapSize = 16;
// Create the toolbar.
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | TBSTYLE_WRAPABLE,
0, 0, 0, 0,
hWndParent, NULL, g_hInst, NULL);
if (hWndToolbar == NULL)
{
return NULL;
}
// Create the imagelist.
HIMAGELIST hImageList = ImageList_Create(
bitmapSize, bitmapSize, // Dimensions of individual bitmaps.
ILC_COLOR16 | ILC_MASK, // Ensures transparent background.
numButtons, 0);
// Set the image list.
SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)ImageListID,
(LPARAM)hImageList);
// Load the button images.
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR,
(LPARAM)HINST_COMMCTRL);
// Initialize button info.
// IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants.
TBBUTTON tbButtons[numButtons] =
{
{ MAKELONG(STD_FILENEW, ImageListID), IDM_NEW, TBSTATE_ENABLED,
buttonStyles, {0}, 0, (INT_PTR)L"New" },
{ MAKELONG(STD_FILEOPEN, ImageListID), IDM_OPEN, TBSTATE_ENABLED,
buttonStyles, {0}, 0, (INT_PTR)L"Open"},
{ MAKELONG(STD_FILESAVE, ImageListID), IDM_SAVE, 0,
buttonStyles, {0}, 0, (INT_PTR)L"Save"}
};
// Add buttons.
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE,
(WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hWndToolbar, TB_ADDBUTTONS, (WPARAM)numButtons,
(LPARAM)&tbButtons);
// Tell the toolbar to resize itself, and show it.
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar, TRUE);
return hWndToolbar;
}
The following example creates the same toolbar in much the same way, but in this case the strings are read from a resource.
HWND CreateToolbarFromResource(HWND hWndParent)
{
// Define some constants.
const int ImageListID = 0;
const int numButtons = 3;
const DWORD buttonStyles = BTNS_AUTOSIZE;
const int bitmapSize = 16;
// Create the toolbar.
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | TBSTYLE_WRAPABLE,
0, 0, 0, 0,
hWndParent, NULL, g_hInst, NULL);
if (hWndToolbar == NULL)
{
return NULL;
}
// Create the imagelist.
HIMAGELIST hImageList = ImageList_Create(
bitmapSize, bitmapSize, // Dimensions of individual bitmaps.
ILC_COLOR | ILC_MASK, // Ensures transparent background.
numButtons, 0);
// Set the image list.
SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)ImageListID,
(LPARAM)hImageList);
// Load the button images.
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR,
(LPARAM)HINST_COMMCTRL);
// Load the text from a resource.
// In the string table, the text for all buttons is a single entry that
// appears as "~New~Open~Save~~". The separator character is arbitrary,
// but it must appear as the first character of the string. The message
// returns the index of the first item, and the items are numbered
// consecutively.
int iNew = SendMessage(hWndToolbar, TB_ADDSTRING,
(WPARAM)g_hInst, (LPARAM)IDS_NEW);
// Initialize button info.
// IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants.
TBBUTTON tbButtons[numButtons] =
{
{ MAKELONG(STD_FILENEW, ImageListID), IDM_NEW, TBSTATE_ENABLED,
buttonStyles, {0}, 0, iNew },
{ MAKELONG(STD_FILEOPEN, ImageListID), IDM_OPEN, TBSTATE_ENABLED,
buttonStyles, {0}, 0, iNew + 1},
{ MAKELONG(STD_FILESAVE, ImageListID), IDM_SAVE, 0,
buttonStyles, {0}, 0, iNew + 2}
};
// Add buttons.
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE,
(WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hWndToolbar, TB_ADDBUTTONS,
(WPARAM)numButtons, (LPARAM)&tbButtons);
// Tell the toolbar to resize itself, and show it.
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar, TRUE);
return hWndToolbar;
}
Creating a Vertical Toolbar
The key to creating a vertical toolbar is to include CCS_VERT in the window style, and to set the TBSTATE_WRAP style for each button.
The following example code creates the vertical toolbar shown in the image.
.png)
HWND CreateVerticalToolbar(HWND hWndParent)
{
// Definition of the buttons.
// IDM_NEW, IDM_0PEN, and IDM_SAVE are application-defined command IDs.
TBBUTTON tbButtons3[numButtons] =
{
{STD_FILENEW, IDM_NEW, TBSTATE_ENABLED | TBSTATE_WRAP, BTNS_BUTTON, {0}, 0L, 0},
{STD_FILEOPEN, IDM_OPEN, TBSTATE_ENABLED | TBSTATE_WRAP, BTNS_BUTTON, {0}, 0L, 0},
{STD_FILESAVE, IDM_SAVE, TBSTATE_ENABLED | TBSTATE_WRAP, BTNS_BUTTON, {0}, 0L, 0}
};
// Create the toolbar window.
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | CCS_VERT | WS_BORDER,
0, 0, 0, 0,
hWndParent,
NULL,
g_hInst,
NULL);
// Create the imagelist.
HIMAGELIST hImageList = ImageList_Create(
24, 24, // Dimensions of individual bitmaps.
ILC_COLOR16 | ILC_MASK, // Ensures transparent background.
numButtons, 0);
// Set the image list.
SendMessage(hWndToolbar, TB_SETIMAGELIST, 0, (LPARAM)hImageList);
// Load the button images.
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_LARGE_COLOR, (LPARAM)HINST_COMMCTRL);
// Add them to the toolbar.
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hWndToolbar, TB_ADDBUTTONS, numButtons, (LPARAM)&tbButtons3);
return hWndToolbar;
}
Dynamically Labeling a Toolbar Button
You can assign text to an existing button by using the TB_SETBUTTONINFO message. The following example demonstrates how to change the text of the third button in the previous examples from "Save" to "Save As".
LRESULT RelabelButton(HWND hWndToolbar)
{
TBBUTTONINFO tbInfo;
tbInfo.cbSize = sizeof(TBBUTTONINFO);
tbInfo.dwMask = TBIF_TEXT;
tbInfo.pszText = L"Save As";
return SendMessage(hWndToolbar, TB_SETBUTTONINFO,
(WPARAM)IDM_SAVE, (LPARAM)&tbInfo);
}
Note Changing a button's text by using
TB_SETBUTTONINFO does not affect the string assigned to that button in the internal string list. Also, if you add a toolbar button string to the internal text list, you cannot retrieve the index of that string by calling
TBN_GETBUTTONINFO; you must use
TB_GETBUTTON instead.
Displaying Tooltips for Buttons
When you specify the TBSTYLE_TOOLTIPS style, the toolbar creates and manages a tooltip control. The tooltip control is hidden and appears only when the user moves the pointer over a toolbar button and leaves it there for approximately one second.
Your application can supply text to the tooltip control in any one of the following ways:
- Set the tooltip text as the iString member of the TBBUTTON structure for each button. You must also send a TB_SETMAXTEXTROWS message and set the maximum text rows to 0, so that the text does not appear as the button label rather than as a tooltip.
- Create the toolbar with the TBSTYLE_LIST style and then set the TBSTYLE_EX_MIXEDBUTTONS extended style. Labels are shown only for buttons that have the BTNS_SHOWTEXT style. For buttons that do not have this style, a tooltip is shown that contains the button text.
- Respond to the TTN_GETDISPINFO notification.
- Respond to the TBN_GETINFOTIP notification.
An application that needs to send messages directly to the tooltip control can retrieve the handle to the control by using the TB_GETTOOLTIPS message. An application can replace the tooltip control of a toolbar with another tooltip control by using the TB_SETTOOLTIPS message.
The most flexible way of supplying tooltip text is to respond to the TTN_GETDISPINFO or TBN_GETINFOTIP notification sent by the toolbar control to its parent in the form of a WM_NOTIFY message.
For TTN_GETDISPINFO, the lParam parameter includes a pointer to an NMTTDISPINFO structure (also defined as LPTOOLTIPTEXT) that specifies the command identifier of the button for which Help text is needed. This identifier is in the NMTTDISPINFO.hdr.idFrom member. An application can copy the Help text to the structure, specify the address of a string containing the Help text, or specify the instance handle and resource identifier of a string resource.
The following example code handles the TTN_GETDISPINFO tooltip notification by providing text from resource identifiers.
case WM_NOTIFY:
switch (((LPNMHDR) lParam)->code)
{
case TTN_GETDISPINFO:
{
LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)lParam;
// Set the instance of the module that contains the resource.
lpttt->hinst = g_hInst;
UINT_PTR idButton = lpttt->hdr.idFrom;
switch (idButton)
{
case IDM_NEW:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_NEW);
break;
case IDM_OPEN:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_OPEN);
break;
case IDM_SAVE:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_SAVE);
break;
}
break;
}
}
return TRUE;
Handling Drop-down Buttons
A drop-down button can present users with a list of options. To create this style of button, specify the BTNS_DROPDOWN style (also called TBSTYLE_DROPDOWN for compatibility with previous versions of the common controls). To show a drop-down button with an arrow, you must also set the TBSTYLE_EX_DRAWDDARROWS toolbar style by sending a TB_SETEXTENDEDSTYLE message.
The following illustration shows a drop-down "Open" button with the context menu open and showing a list of files. In this example, the toolbar has the TBSTYLE_EX_DRAWDDARROWS style.
.png)
The following illustration shows the same toolbar, this time without the TBSTYLE_EX_DRAWDDARROWS style.
.png)
When the user clicks a toolbar button that uses the BTNS_DROPDOWN style, the toolbar control sends its parent a TBN_DROPDOWN notification message.
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)
switch(lpnm->code)
{
case TBN_DROPDOWN:
{
// Get the coordinates of the button.
RECT rc;
SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT,
(WPARAM)lpnmTB->iItem, (LPARAM)&rc);
// Convert to screen coordinates.
MapWindowPoints(lpnmTB->hdr.hwndFrom,
HWND_DESKTOP, (LPPOINT)&rc, 2);
// Get the menu.
HMENU hMenuLoaded = LoadMenu(g_hinst, MAKEINTRESOURCE(IDR_POPUP));
// Get the submenu for the first menu item.
HMENU hPopupMenu = GetSubMenu(hMenuLoaded, 0);
// Set up the popup menu.
// Set rcExclude equal to the button rectangle so that if the toolbar
// is too close to the bottom of the screen, the menu will appear above
// the button rather than below it.
TPMPARAMS tpm;
tpm.cbSize = sizeof(TPMPARAMS);
tpm.rcExclude = rc;
// Show the menu and wait for input.
// If the user selects an item, its WM_COMMAND is sent.
TrackPopupMenuEx(hPopupMenu,
TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
rc.left, rc.bottom, g_hwndMain, &tpm);
DestroyMenu(hMenuLoaded);
return (FALSE);
}
}
return FALSE;
}
Customizing Toolbars
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.
Note Toolbars in dialog boxes cannot be customized.
To enable customization, include the CCS_ADJUSTABLE common controls style flag when you create the toolbar control. There are two basic approaches to customization:
- The customization dialog box. This system-provided dialog box is the simplest approach. It gives users a graphical user interface that allows them to add, delete, or move icons.
- Dragging and dropping tools. Implementing drag-and-drop functionality allows users to move tools to another location on the toolbar or delete them by dragging them off the toolbar. It provides users a quick and easy way to organize their toolbar, but does not allow them to add tools.
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 functionality.
Customization Dialog Box
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 programmatically by sending the toolbar control a TB_CUSTOMIZE message.
The following illustration shows an example of the toolbar customization dialog box.
.png)
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 (other than with the Separator, which appears automatically).
The toolbar control notifies your application that it is launching a customization dialog box by sending its parent window a TBN_BEGINADJUST notification followed by a TBN_INITCUSTOMIZE notification. In most cases, the application does not need to respond to these notifications. However, if f you don't want the Customize Toolbar dialog box to display a Help button, handle TBN_INITCUSTOMIZE by returning TBNRF_HIDEHELP.
The toolbar control then collects the information it needs to initialize the dialog box by sending three series of notifications in the following order:
- A TBN_QUERYINSERT notification for each button on the toolbar to determine where buttons can be inserted. Return FALSE to prevent a button from being inserted to the left of the button specified in the notification. If you return FALSE to all TBN_QUERYINSERT notifications, the dialog box will not be displayed.
- A TBN_QUERYDELETE notification for each tool currently on the toolbar. Return TRUE if a tool can be deleted, or FALSE if not.
- A series of TBN_GETBUTTONINFO notifications to populate the list of available buttons. To add a button to the list, fill in the NMTOOLBAR structure that is passed with the notification and return TRUE. When you have no more tools to add, return FALSE. Note that you can return information for buttons that are already on the toolbar; these buttons will not be added to the list.
The dialog box is then displayed, and the user can begin to customize the toolbar.
When the dialog box is open, your application can receive a variety of notifications, depending on the user's actions:
- TBN_QUERYINSERT. Each time the user changes the location of a tool on the toolbar, or adds a tool. Return FALSE to prevent the tool from being inserted at that location.
- TBN_DELETINGBUTTON. The user is about to remove a tool from the toolbar.
- TBN_CUSTHELP. The user has clicked the Help button.
- TBN_TOOLBARCHANGE. The user has added, moved, or deleted a tool.
- TBN_RESET. The user has clicked the Reset button.
After the dialog box is destroyed, your application will receive a TBN_ENDADJUST notification.
The following code example shows one way to implement toolbar customization.
// The buttons are kept in an array of TBBUTTON structures.
//
// STD_FILENEW and so on are standard identifiers for the
// system-defined bitmaps that have already been assigned as the toolbar's
// image list.
//
// IDM_NEW and so on are application-defined command identifiers.
TBBUTTON allButtons[] =
{
{ MAKELONG(STD_FILENEW, ImageListID), IDM_NEW, TBSTATE_ENABLED, 0, {0},
0, (INT_PTR)L"New" },
{ MAKELONG(STD_FILEOPEN, ImageListID), IDM_OPEN, TBSTATE_ENABLED, 0, {0},
0, (INT_PTR)L"Open"},
{ MAKELONG(STD_FILESAVE, ImageListID), IDM_SAVE, TBSTATE_ENABLED, 0, {0},
0, (INT_PTR)L"Save"},
{ MAKELONG(STD_CUT, ImageListID), IDM_CUT, TBSTATE_ENABLED, 0, {0},
0, (INT_PTR)L"Cut" },
{ MAKELONG(STD_COPY, ImageListID), IDM_COPY, TBSTATE_ENABLED, 0, {0},
0, (INT_PTR)L"Copy"},
{ MAKELONG(STD_PASTE, ImageListID), IDM_PASTE, TBSTATE_ENABLED, 0, {0},
0, (INT_PTR)L"Paste"}
};
// The following appears in the window's message handler.
case WM_NOTIFY:
{
switch (((LPNMHDR)lParam)->code)
{
case TBN_GETBUTTONINFO:
{
LPTBNOTIFY lpTbNotify = (LPTBNOTIFY)lParam;
// Pass the next button from our array. There is no need to
// filter out buttons that are already used; they will be
// ignored.
int buttonCount = sizeof(allButtons) / sizeof(TBBUTTON);
if (lpTbNotify->iItem < buttonCount)
{
lpTbNotify->tbButton = allButtons[lpTbNotify->iItem];
return TRUE;
}
else
{
// No more buttons.
return FALSE;
}
}
break;
case TBN_QUERYINSERT:
case TBN_QUERYDELETE:
return TRUE;
}
}
Dragging and Dropping Tools
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:
- The TBN_QUERYDELETE notification is sent as soon as the user attempts to move the button, before the ghost button is displayed. Return FALSE to prevent the button from being moved. If you return TRUE, the user will be able to either move the tool or delete it by dropping it off the toolbar. If a tool can be moved, it can be deleted. However, if the user deletes a tool, the toolbar control will send your application a TBN_DELETINGBUTTON notification, at which point you can choose to reinsert the button at its original location, thereby canceling the deletion.
- The TBN_QUERYINSERT notification is sent when the user attempts to drop the button on the toolbar. To prevent the button being moved from being dropped to the left of the button specified in the notification, return FALSE. This notification is not sent if the user drops the tool off the toolbar.
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 TBN_BEGINDRAG notification to indicate the start of a drag operation, and a TBN_ENDDRAG notification to indicate the end. If you want to enable this form of drag-and-drop, your application must handle these notifications, provide the necessary user interface, and modify the toolbar to reflect any changes.
Saving and Restoring Toolbars
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 TB_SAVERESTORE message with the lParam set to TRUE. The lParam value of this message specifies whether you are requesting a save or a restore operation. Once the message is sent, there are two ways to handle the save/restore operation:
- With common controls version 4.72 and earlier, you must implement a TBN_GETBUTTONINFO handler. The toolbar control sends this notification to request information about each button as it is restored.
- Version 5.80 includes a save/restore option. At the beginning of the process, and as each button is saved or restored, your application will receive a TBN_SAVE or TBN_RESTORE notification. To use this option, you must implement notification handlers to provide the bitmap and state information needed to successfully save or restore the toolbar state.
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.
How to Handle a TBN_SAVE Notification
The first TBN_SAVE notification is sent at the beginning of the save process. Before any buttons are saved, the members of the NMTBSAVE structure are set as shown in the following table.
| 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 | TBBUTTON structure that describes the button being saved. |
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.
Restoring Saved Toolbars
The restore process basically reverses the save process. At the beginning, your application will receive a NMTBRESTORE notification with the iItem member of the NMTBRESTORE structure set to -1. The cbData member is set to the size ofpData, and cButtons is set to the number of buttons.
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 example 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;
}
Embedding Nonbutton Controls in Toolbars
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.
.png)
Note Consider using
Rebar Controls instead of placing controls in toolbars.
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, setting the width of the separator to the number of pixels you want to reserve.
The following example function creates the toolbar in the preceding illustration.
// IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined
// command constants.
HWND CreateToolbarWithEdit(HWND hWndParent)
{
// Define some constants.
const int ImageListID = 0;
const int bitmapSize = 16;
// Dimensions of edit control.
const int cx_edit = 100;
const int cy_edit = 35;
// Toolbar buttons
TBBUTTON tbButtons[] =
{
// The separator is set to the width of the edit control.
{cx_edit, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, -1},
{STD_FILENEW, IDM_NEW, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
{STD_FILEOPEN, IDM_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
{STD_FILESAVE, IDM_SAVE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
{0, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},
};
// Create the toolbar.
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, L"Toolbar",
WS_CHILD | WS_VISIBLE | WS_BORDER,
0, 0, 0, 0,
hWndParent, NULL, HINST_COMMCTRL, NULL);
if (!hWndToolbar)
{
return NULL;
}
int numButtons = sizeof(tbButtons) / sizeof(TBBUTTON);
// Create the imagelist.
HIMAGELIST hImageList = ImageList_Create(
bitmapSize, bitmapSize, // Dimensions of individual bitmaps.
0, // Flags.
numButtons, 0);
// Set the image list.
SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)ImageListID, (LPARAM)hImageList);
// Load the button images.
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
// Add buttons.
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hWndToolbar, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);
// Create the edit control child window.
HWND hWndEdit = CreateWindowEx(0L, L"Edit", NULL,
WS_CHILD | WS_BORDER | WS_VISIBLE | ES_LEFT
| ES_AUTOVSCROLL | ES_MULTILINE,
0, 0, cx_edit, cy_edit,
hWndToolbar, (HMENU) IDM_EDIT, g_hInst, 0 );
if (!hWndEdit)
{
DestroyWindow(hWndToolbar);
return NULL;
}
// 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 as follows:
SetParent (hWndEdit, hWndToolbar);
Notifications go to the original parent. Therefore the edit control messages go to the parent of the toolbar even though the edit window resides in the toolbar window.
Using Hot-tracking with Toolbars
When a mouse pointer hovers over an item, the item becomes hot. If hot-tracking is enabled, the hot item highlighted.
A toolbar created with the TBSTYLE_FLAT style, or one that uses Visual Styles, supports hot-tracking by default.
Hot-tracking requires that you create image lists; therefore, you cannot use TB_ADDBITMAP or CreateToolbarEx to create your toolbar.
When the mouse hovers over a toolbar button, the button is outlined to highlight it. The following illustration shows a toolbar with hot-tracking enabled; the mouse pointer was hovering on the Save button when the screen shot was taken..
.png)
If you want a toolbar button bitmap to change when the state of the control changes, store the different images in Image Lists. For example, some applications have black and white toolbar buttons that become colored when they are selected. The two different images are stored in image lists. Toolbars support using up to three image lists. Typically an application has a default, disabled, and hot-tracking list of images. To set and retrieve image lists for hot toolbar buttons, use TB_SETHOTIMAGELIST and TB_GETHOTIMAGELIST. The following code example creates, fills, and assigns an image list for hot buttons.
// 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);