WinAPI 钢筋控件未显示

WinAPI rebar control not showing up

我正在使用 C++ 开发一个纯 WinAPI 应用程序,我想添加一个工具栏,该工具栏 inside/on 是一个 rebar 控件。我可以创建并添加工具栏,但无法显示钢筋(即使我设置了 RBBS_GRIPPERALWAYS 样式,也没有夹具):

我从有关 toolbar/rebar 控件的 MSDN 页面中获取代码并组装了一个最小示例,但仍然没有成功。 Return CreateWindow 和 SendMessage 等代码对我来说没问题。我也尝试了 this question 的解决方案,但无论如何我都无法让它工作。任何帮助将不胜感激。这是完整的源代码:

#include "targetver.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <CommCtrl.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include "Resource.h"
#pragma comment(lib, "comctl32.lib")

// Enable visual styles
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

// Toolbar defines
#define IDM_NEW 100
#define IDM_OPEN 101
#define IDM_SAVE 102
#define NUM_TBBUTTONS 3

// Global variables
HINSTANCE g_hInst;
HIMAGELIST g_hImageList = NULL;

// Forward declarations
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND CreateSimpleToolbar(HWND hwndParent);
HWND CreateSimpleRebar(HWND hwndParent, HWND hwndToolbar);

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) {
    // Initialize common controls.
    INITCOMMONCONTROLSEX icex;
    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icex.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES;
    InitCommonControlsEx(&icex);

    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_REBARTEST));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_REBARTEST);
    wcex.lpszClassName = _T("RebarTest");
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    RegisterClassExW(&wcex);
    
    HWND hwndMainWindow = CreateWindowW(_T("RebarTest"), _T("AppTitle"), 
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
        nullptr, nullptr, hInstance, nullptr);
    if (!hwndMainWindow)
        return FALSE;

    ShowWindow(hwndMainWindow, nCmdShow);
    UpdateWindow(hwndMainWindow);

    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    static HWND hwndToolbar, hwndRebar;
    switch (message)
    {
    case WM_CREATE:
    {
        hwndToolbar = CreateSimpleToolbar(hwnd);
        hwndRebar = CreateSimpleRebar(hwnd, hwndToolbar);
    }
    break;
    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // Menüauswahl analysieren:
        switch (wmId)
        {       
        case IDM_EXIT:
            DestroyWindow(hwnd);
            break;
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

HWND CreateSimpleToolbar(HWND hwndParent) {
    // 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 image list.
    g_hImageList = ImageList_Create(16, 16, ILC_COLOR16 | ILC_MASK, NUM_TBBUTTONS, 0);

    // Set the image list and add buttons
    const int imageListID = 0;
    SendMessage(hwndToolbar, TB_SETIMAGELIST, (WPARAM)imageListID, (LPARAM)g_hImageList);
    SendMessage(hwndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);

    // Initialize button info.
    TBBUTTON tbButtons[NUM_TBBUTTONS] =
    {
        { MAKELONG(STD_FILENEW,  imageListID), IDM_NEW,  TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)L"New" },
        { MAKELONG(STD_FILEOPEN, imageListID), IDM_OPEN, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)L"Open"},
        { MAKELONG(STD_FILESAVE, imageListID), IDM_SAVE, 0,               BTNS_AUTOSIZE, {0}, 0, (INT_PTR)L"Save"}
    };

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

    // Resize the toolbar, and then show it.
    SendMessage(hwndToolbar, TB_AUTOSIZE, 0, 0);
    ShowWindow(hwndToolbar, TRUE);
    return hwndToolbar;
}

HWND CreateSimpleRebar(HWND hwndParent, HWND hwndToolbar) {
    // Check parameters.
    if (!hwndParent || !hwndToolbar)
        return NULL;

    // Create the rebar.
    HWND hwndRebar = CreateWindowEx(WS_EX_TOOLWINDOW,
        REBARCLASSNAME, NULL,
        WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 
        RBS_VARHEIGHT | CCS_NODIVIDER | RBS_BANDBORDERS,
        0, 0, 0, 0,
        hwndParent, NULL, g_hInst, NULL);

    if (!hwndRebar)
        return NULL;

    // Get the height of the toolbar.
    DWORD dwBtnSize = (DWORD)SendMessage(hwndToolbar, TB_GETBUTTONSIZE, 0, 0);
    
    REBARBANDINFO rbBand;
    rbBand.cbSize = sizeof(REBARBANDINFO);
    rbBand.fMask =
        RBBIM_STYLE       // fStyle is valid.
        | RBBIM_TEXT        // lpText is valid.
        | RBBIM_CHILD       // hwndChild is valid.
        | RBBIM_CHILDSIZE   // child size members are valid.
        | RBBIM_SIZE;       // cx is valid
    rbBand.fStyle = RBBS_CHILDEDGE | RBBS_GRIPPERALWAYS;
    rbBand.lpText = (LPWSTR)_T("");
    rbBand.hwndChild = hwndToolbar;
    rbBand.cyChild = LOWORD(dwBtnSize);
    rbBand.cxMinChild = NUM_TBBUTTONS * HIWORD(dwBtnSize);
    rbBand.cyMinChild = LOWORD(dwBtnSize);
    rbBand.cx = 0;  // The default width is the width of the buttons.

    // Add the band with the toolbar.
    SendMessage(hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
    return hwndRebar;
}

this question, you need to have a _WIN32_WINNT defined, which I assume you may have in your "targetver.h" header. Since you are declaring common-controls version 6, I think you need to target WS-2003/Vista at a minimum, according to this 评论中所述。我首先用你的代码尝试了 #define WINVER 0x0601#define _WIN32_WINNT_WIN7 0x0601,它在我的机器上运行。

我认为您链接的问题有点过时了,this table 说 common-controls 版本 6 的结构大小应该是 REBARBANDINFO_V6_SIZE。但是,在使用该结构大小时,我遇到了您的链接问题中的 4 像素高度问题。保留您的 sizeof 代码 as-is 对我有用。

我认为您遇到的真正问题是您的工具栏正在处理其自身的大小调整和定位。如this About Toolbar Control page所述,您需要更改此

HWND hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0, hwndParent, NULL, g_hInst, NULL);

至此

HWND hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_WRAPABLE | CCS_NORESIZE | CCS_NOPARENTALIGN, 0, 0, 0, 0, hwndParent, NULL, g_hInst, NULL);

在您的 CreateSimpleToolbar 函数中,这样宿主 rebar 可以控制工具栏的大小和位置。