c++cwinapicontrolswin32gui

WinAPI rebar control not showing up


I'm working on a pure WinAPI application in C++ and I want to add a toolbar that lives inside/on a rebar control. I can create and add the toolbar but I can't get the rebar to show up (there are no grippers even though I set the RBBS_GRIPPERALWAYS style):

Screenshot

I took the code from the MSDN pages about toolbar/rebar controls and assembled a minimal sample but still no success. Return codes of CreateWindow and SendMessage etc. look okay to me. I also tried the solution of this question but I can't get it working anyways. Any help would be greatly appreciated.. here's the complete source code:

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

Solution

  • As described in the comments on 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. I first tried #define WINVER 0x0601 and #define _WIN32_WINNT_WIN7 0x0601 with your code and it worked on my machine.

    I think the question you linked is a little out of date, this table says the structure size for common-controls version 6 should be REBARBANDINFO_V6_SIZE. However, I encountered the 4 pixel height issue from your linked question when using that structure size. Keeping your sizeof code as-is worked for me.

    I think the real issue you have is your toolbar is handling its own resizing and positioning. As described on this About Toolbar Control page, you need to change this

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

    to this

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

    in your CreateSimpleToolbar function so the host rebar can take control of the toolbar sizing and positioning.