Windows工具栏 - 控制按钮大小和填充

时间:2014-09-15 19:31:59

标签: c++ windows winapi bitmap padding

我试图理解Windows工具栏的行为 - 特别是以下值如何相互作用:

  • 使用的位图图像的大小
  • 工具栏按钮的有效大小
  • 图像与按钮边缘之间的填充
  • 工具栏的高度

按钮显示的文字与我的情况无关。

我真正想要做的是为用户提供一个选项,这样他就可以从几个工具栏按钮大小中选择(显示比如16x16,32x32或48x48像素的位图),并在选项值后相应地重新显示工具栏变化。这是通过销毁工具栏的图像列表并使用适当的位图重建它们来实现的。我目前遇到的问题是,当从大小16切换到48并返回到大小16时,工具栏看起来与以前略有不同。

这是应用程序启动时工具栏的样子(正确):

Before switching the toolbar size

一旦我切换到48号并再次返回,它看起来像这样(错误):

After switching to a larger size and back

所有按钮都比以前更高,每个下拉按钮在其位图及其下拉箭头周围都有额外的空间。

(出于测试目的,工具栏已经足够高,可以容纳所有按钮大小而不需要增加高度。这是为了排除按钮大小的变化源于可能的工具栏调整大小的必要性,暂时需要切换到48号。)

看起来好像在按钮位图和按钮边缘之间呈现了额外的填充 - 好像用较大的位图/按钮重建工具栏会导致Windows在内部增加填充(这是有意义的),但不会减少它我随后用较小的位图/按钮重建工具栏。但是,发送TB_GETPADDING始终返回0x00060007,这表示16x16位图的标准(正确)填充已到位。

为了尝试通过自己设置填充来解决问题,我在所有非分隔符按钮上设置了TBSTYLE_AUTOSIZE样式(这是为了应用填充所必需的)。使用这种样式,甚至没有调用TB_SETPADDING,切换到48后再返回,工具栏看起来像这样:

After switching to a larger size and back, with TBSTYLE_AUTOSIZE

在这种情况下,按钮高度也是错误的。

问题是:在重建图像列表后导致按钮显示的原因是什么?

有些注意事项:

  • 构建工具栏时,我调用TB_SETBITMAPSIZE,但是既没有TB_SETBUTTONSIZE也没有TB_SETPADDING,因为位图大小就是我所拥有的,并且我假设按钮大小将从中正确导出。
  • 我知道我可以从头开始构建整个工具栏窗口(不仅仅是图像列表),但我想避免这种情况,所以我可以继续使用相同的工具栏窗口句柄。
  • 我了解CCS_NORESIZE工具栏样式(当前已设置)和TB_AUTOSIZE消息,但使用它们进行实验并未获得任何见解。

2 个答案:

答案 0 :(得分:4)

常见控件一直是Windows中的主要错误工厂。微软在6个主要Windows版本和10个版本的comctl32.dll中保持兼容存在很大问题。特别是视觉风格的渲染器一直是一个问题点。

核心问题是18年前他们的api已经陷入困境,没有合理的方式让它与第一次发布时的工作方式不同。他们的代码获得了大量的appcompat hacks来实现这一目标。这样的hack将例如对先前版本返回的值进行处理,以便客户端程序不知道并且不需要知道它正在使用与其测试的版本非常不同的版本。

这会产生副作用,当您以不同寻常的方式使用控件时,您会发现这种副作用,这种方式与肉食和土豆Windows程序通常使用的方式非常不同。完全像你的场景。您正在与工具栏的内部状态作斗争的非常高的几率,当您切换大小时,您无法看到这些状态并且无法正确恢复。相当不可判,内部状态根本不可见。除了不良副作用外。

解决方案是您已经知道的解决方案。从头开始重新创建工具栏。这样就不会出错。

答案 1 :(得分:3)

我无法说出问题是什么(问题中没有代码),但最有可能 破坏图像列表的解决方案会导致这种情况。你不需要破坏列表 但要删除按钮然后添加新按钮。波纹管代码工作正常:

创建工具栏

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*/}

为您的图片创建 ImageList'

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);

将图像添加到列表中:

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);

与大型(48x48)相同

g_hImageListSmall 添加到 ToolBar 以便开始:

//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);

这是第一步。

写两个函数:

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;
}

如果您想更改 ToolBar 中按钮的大小:

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

参考文献:

How to Create Toolbars
How to Customize Toolbars