In this tip, I present a solution for putting a new image on an existing toolbar button.
Introduction
I've searched the web, but I haven't found a satisfying recipe for putting a new image on an existing toolbar button - even the Microsoft documentation was of limited help.
All of you who are facing a similar problem, I would like to present my solution here.
Background
Attempt 1: There is the TB_CHANGEBITMAP
message available, but before using it - the image list of the toolbar must be adjusted with ImageList_Add()
. This may cause a realloc of the image list in case its capacity is exhausted and some sources say you may have to rebind the image list to the toolbar. I did not manage to get this to work, the TB_CHANGEBITMAP
message always returned FALSE
.
Attempt 2: To work around the TB_CHANGEBITMAP
message, I tried the TB_GETBUTTONINFO
and TB_SETBUTTONINFO
messages. These messages return TRUE
, but after the calls, the old image was gone and the toolbar button only showed the background color.
Attempt 3: Only after that, I had the idea to simply exchange the image in the image list of the toolbar, whereby the image index does not change, the image list size does not change and no changes have to be made to the toolbar button.
A first positive side effect of this approach is that the image list does not increase in size even when the image is swapped several times.
Furthermore, ImageList_Add()
always creates its own copies of the color bitmap and mask bitmap for the image list. On the one hand, this keeps the responsibility for the original HBITMAP
handles in the application and does not pass them to the windows manager, making it easier to keep the application code clean. On the other hand, it is no longer possible to find an earlier bitmap after the ImageList_Add()
call with the help of the original HBITMAP
handles (e.g., to swap the image back).
The second positive side effect of this approach is that it is not necessary to find previous bitmaps at all.
Using the Code
The following method swaps the bitmaps of a toolbar button in the toolbar's image list and deletes the bitmaps previously registered for the button.
int ReplaceButtonImage(UINT uiCommandID, HBITMAP hbmpDDBImage,
HBITMAP hbmpDDBMask)
{
TBBUTTONINFO tbi;
tbi.cbSize = sizeof(TBBUTTONINFO);
tbi.dwMask = TBIF_IMAGE | TBIF_COMMAND;
if (::SendMessage((HWND)_hResObj, TB_GETBUTTONINFO,
(WPARAM)uiCommandID, (LPARAM)&tbi) < 0)
return -3;
int iImageCount = ::ImageList_GetImageCount(_hEnabledImageList);
if (tbi.iImage < 0 || tbi.iImage >= iImageCount)
return -2;
if (hbmpDDBImage == NULL)
return -2;
IMAGEINFO ii;
HBITMAP hbmpDDBImageOld = NULL;
HBITMAP hbmpDDBMaskOld = NULL;
if (::ImageList_GetImageInfo(_hEnabledImageList, tbi.iImage, &ii) == TRUE)
{
hbmpDDBImageOld = ii.hbmImage;
hbmpDDBMaskOld = ii.hbmMask;
}
if (::ImageList_Replace(_hEnabledImageList, tbi.iImage,
hbmpDDBImage, hbmpDDBMask) == TRUE)
{
if (hbmpDDBImageOld != NULL)
::DeleteObject((HGDIOBJ)hbmpDDBImageOld);
if (hbmpDDBMaskOld != NULL)
::DeleteObject((HGDIOBJ)hbmpDDBMaskOld);
return tbi.iImage;
}
return -1;
}
Points of Interest
In the end, the simplest solution turned out to be the best solution.
History
- 22nd February, 2021: Initial version