c++ - Windows Toolbar - Controlling button size and padding


Question: 

I'm trying to understand the behaviour of a Windows toolbar - in particular how the following values interact:

  • the size of the bitmap image used
  • the effective size of a toolbar button
  • the padding between the image and the button edge
  • the height of the toolbar

Text displayed by a button is not relevant in my case.

What I actually want to do is provide an option for the user so he can choose from several toolbar button sizes (that will display bitmaps of say, 16x16, 32x32, or 48x48 pixels) and redisplay the toolbar accordingly after the option value changes. This is implemented by destroying the toolbar's image lists and rebuilding them with the appropriate bitmaps. The problem I currently have is that when switching from size 16 to 48 and back to size 16, the toolbar looks slightly different than before.

This is what the toolbar looks like when the application starts (correct):

Before switching the toolbar size

Once I switch to size 48 and back again, it looks like this (wrong):

After switching to a larger size and back

All buttons are higher than before, and each dropdown button has additional space around its bitmap and its dropdown arrow.

(For testing purposes, the toolbar has been made high enough to accomodate all button sizes without requiring an increase in height. This is to rule out the possibility that the change in button size stems from a possible toolbar resize, necessitated by temporarily switching to size 48.)

It looks as if additional padding were being rendered between a button bitmap and the button edge - as if rebuilding the toolbar with larger bitmaps/buttons caused Windows to internally increase the padding (which would make sense), but not decrease it when I subsequently rebuild the toolbar with the smaller bitmaps/buttons. However, sending TB_GETPADDING always returns 0x00060007, which indicates that the standard (correct) padding for 16x16 bitmaps is in place.

In an attempt to solve the problem by setting padding myself, I set the TBSTYLE_AUTOSIZE style on all non-separator buttons (this is required in order to apply padding). With this style, without even calling TB_SETPADDING, after switching to size 48 and back again, the toolbar looks like this:

After switching to a larger size and back, with TBSTYLE_AUTOSIZE

In this case, the button height is also wrong.

The question is: What is causing the buttons to be displayed differently after rebuilding the image lists?

Some aside notes:

  • When building the toolbar, I call TB_SETBITMAPSIZE, but neither TB_SETBUTTONSIZE nor TB_SETPADDING, because the bitmap size is all I have, and I assumed the button size would be derived correctly from that.
  • I'm aware I could simply build the entire toolbar window from scratch (not just the image lists), but would like to avoid that, so I can keep working with the same toolbar window handle.
  • I'm aware of the CCS_NORESIZE toolbar style (it's currently set) and the TB_AUTOSIZE message, but experiments with them have not led to any insights.



2 Answers: 

I can't say what is the problem(there is no code in the question) but it is most probable that the solution of destroying the list of images causes this. You dont need to destroy the lists but to remove the buttons and then add new ones. The bellow code works fine:

Create ToolBar:

if((toolBarHwnd = CreateWindowEx(
                0,
                TOOLBARCLASSNAME,,
                NULL,
                WS_VISIBLE | WS_CHILD | TBSTYLE_WRAPABLE,
                0,
                0, //820,
                0,
                0,
                winHwnd, //main window
                (HMENU)IDC_TOOLBAR,
                hThisInstance,
                NULL
            )) == NULL){/*Error*/}

Create ImageList's for your images:

HIMAGELIST g_hImageListSmall = NULL, g_hImageListMedium = NULL, g_hImageListLarge = NULL;
int numButtons = 3
g_hImageListSmall = ImageList_Create(16, 16,   // Dimensions of individual bitmaps.
                                ILC_COLOR16 | ILC_MASK,   // Ensures transparent background.
                                numButtons, 0);
g_hImageListMedium = ImageList_Create(32, 32,
                                ILC_COLOR16 | ILC_MASK,
                                numButtons, 0);
g_hImageListLarge = ImageList_Create(48, 48,
                                ILC_COLOR16 | ILC_MASK,
                                numButtons, 0);

Add images to the lists:

HBITMAP hBitmapImageSmall = NULL, hBitmapImageMedium = NULL, hBitmapImageLarge = NULL;
hBitmapImageSmall = LoadImage(NULL, L"....YourBitmap.bmp", IMAGE_BITMAP, 16, 16, 0x10);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL); //I am using the same image

hBitmapImageMedium = LoadImage(NULL, L"....YourBitmap.bmp", IMAGE_BITMAP, 32, 32, 0x10);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);

The same with the large one(48x48)

Add g_hImageListSmall to the ToolBar for start:

//Set the image list.
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListSmall);

// Initialize button info.
// IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants.
TBBUTTON tbButtons[numButtons] = 
{
    { 0, IDM_NEW,  TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL },
    { 1, IDM_OPEN, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL},
    { 2, IDM_SAVE, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL}
};

// Add buttons.
SendMessage(toolBarHwnd, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(toolBarHwnd, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);

// Resize the toolbar
SendMessage(toolBarHwnd, TB_AUTOSIZE, 0, 0);

That is the first step.

Write two functions:

void RemoveButtons(void){
    int nCount, i;

    // Remove all of the existing buttons, starting with the last one.
    nCount = SendMessage(toolBarHwnd, TB_BUTTONCOUNT, 0, 0);

    for(i = nCount - 1; i >= 0; i--){ SendMessage(toolBarHwnd, TB_DELETEBUTTON, i, 0); }

    return;
}

enum{SMALL, MEDIUM, LARGE};

void AddButtons(int sizeButtons){
    if(sizeButtons == SMALL){
        SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListSmall);
    }
    else if(sizeButtons == MEDIUM){
        SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListMedium);
    }
    else{
        SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListLarge);
    }

    // Add buttons.
    SendMessage(toolBarHwnd, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
    SendMessage(toolBarHwnd, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);

    // Resize the toolbar
    SendMessage(toolBarHwnd, TB_AUTOSIZE, 0, 0); 

    return;
}

When ever you want to change the size of the buttons in ToolBar:

RemoveButtons();
AddButtons(LARGE); //or SMALL, MEDIUM

References:

How to Create Toolbars
How to Customize Toolbars

 

The common controls have been a major bug factory in Windows. Microsoft has had a great deal of trouble keeping them compatible across 6 major Windows releases and 10 versions of comctl32.dll. Particularly the visual style renderers have been a problem spot.

Core issue is that the api for them was set in stone 18 years ago with no reasonable way to make it work differently from the way it worked in their first release. Their code acquired a great many deal of appcompat hacks to achieve this. Such an hack will for example doctor a value that was returned by the previous version so that the client program has no idea, and doesn't need to know, that it is working with a very different version from the one it was tested against.

This has side-effects, the kind you'll discover when you use the controls in an unusual way that's very different from the way they are normally used by meat-and-potatoes Windows programs. Exactly like your scenario. Very high odds that you are battling internal state of the toolbar that you cannot see and doesn't get properly restored when you switch sizes. Quite undebuggable, that internal state isn't visible at all. Other than from the undesirable side-effects.

The solution is the one you already know. Recreate the toolbar from scratch. It can't go wrong that way.

 

More Articles


android - Different toolbar for each fragment in navigation drawer

I have a MainActivity which has a NavigationDrawer. This NavigationDrawer is Synced with the Toolbar initially. And i want to use the Only one NavigationDrawer across the whole Application. Now the Problem is that each fragment has the different toolbar or CollapsingToolbar.I already read the same

Smooth keydown animation on Canvas in JavaScript

I'm very new to programming and I'm trying to create some code that will allow me to move a square around the Canvas by pressing arrow keys. I'm able to get the square to move, but its motion isn't very smooth. I have it moving by increments of 10 pixels at a time, so I understand why it feels kind

android - hide toolbar when collapsing collapsingtoobarlayout

What I am trying to achieve is hide the collapsingtoolbarlayout when scrolling my recyclerview and collapse the maintoolbar if scrolled further. but I can achieve only upto this http://i.imgur.com/t6wTW5H.gif.<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="htt


javascript - get a smooth animation for a canvas game

How to get a better animation, dinamically, even when browser is busy or idle, for different devices which have different hardware capacity.I have tried many ways and still cannot find the right way to make the game to display a better animation.This is what i tried:var now;var then = Date.now();var

android - How to create bottomsheet with toolbar when expanded,

I want to create bottom sheet layout, which when expanded to full screen should display toolbar. I have used following code but it is displaying toolbar even if its not expanded to full screen.<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:an

compact framework - Toolbar button sizes in windows mobile 6.5.3

We have a VB.net CF2.0 Apllication that up until now has run on windows mobile platforms from 2003 to 6.0 without any major issues. Our current team use HP iPAQ devices running winodws 6.0 and everything runs fine. There is a good, hardware related reson why we use these devices. We are looking to u


wpf - Giving a stackPanel the same LinearGradientBrush as the toolbar has by default

I have two ToolBars side by side (I had to do that so I could align buttons right and left)But now, I have to add some text that can be aligned right left or center between both toolbars. I added the text in a TextBlock inside a StackPanel disposed on a Grid at the second position (between the toolb

formatting - Excel 2007 floating format toolbar customisation

Is it possible in Excel 2007 to customise the floating format bar that is shown when you right-click on a cell? To avoid confusion, I don't mean the "Cell" commandbar menu, but the second floating toolbar with formatting buttons.e.g. is it possible to add a Styles dropdown, or have any other text al

jquery - CSS for toolbar with UI Slider centered between left and right buttons

I'm attempting to create a 100% width toolbar. This toolbar needs to have a variable number of buttons aligned to the left side, as well as a variable number of buttons aligned to the right. That's the easy part.But now I want to put a jQuery UI slider in the center that takes up the full remaining

WPF c# window navigation

i got 2 window , i want to navigate from one another but i when i debug it , it shows that nav is null , why is that so? Does it suppose to be null? because i used the same way to navigate in the past and it work and i try it now , it doesn't work. But if i put nav == null , it says nav.Navigate(n