【问题标题】:Windows Toolbar - Controlling button size and paddingWindows 工具栏 - 控制按钮大小和填充
【发布时间】:2014-09-15 19:31:59
【问题描述】:

我正在尝试了解 Windows 工具栏的行为 - 特别是以下值如何交互:

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

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

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

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

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

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

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

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

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

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

问题是:是什么导致重建图像列表后按钮显示不同?

一些旁注:

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

【问题讨论】:

    标签: c++ windows winapi bitmap padding


    【解决方案1】:

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

    创建工具栏

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

    当您想更改工具栏中按钮的大小时:

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

    参考资料:

    How to Create Toolbars
    How to Customize Toolbars

    【讨论】:

      【解决方案2】:

      通用控件一直是 Windows 中的主要错误工厂。 Microsoft 在保持它们在 6 个主要 Windows 版本和 10 个 comctl32.dll 版本之间兼容时遇到了很大的麻烦。特别是视觉风格渲染器一直是个问题。

      核心问题是他们的 api 是在 18 年前一成不变的,没有合理的方法使它的工作方式与他们第一次发布时的工作方式不同。他们的代码获得了大量的 appcompat hack 来实现这一点。例如,这样的 hack 会篡改以前版本返回的值,以便客户端程序不知道也不需要知道它正在使用与测试版本完全不同的版本。

      这有副作用,当您以一种不寻常的方式使用控件时会发现这种副作用,这种方式与肉类和土豆 Windows 程序通常使用的方式非常不同。和你的场景完全一样。很有可能您正在与工具栏的内部状态作斗争,当您切换大小时,您看不到并且无法正确恢复。完全不可调试,内部状态根本不可见。除了不良的副作用。

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

      【讨论】:

      • 由于这些控制,我有些头疼。更不用说一些 DLL Hell 碍事了,只是为了好玩。
      猜你喜欢
      • 2017-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多