user5310788
user5310788

Reputation:

Strange effect when resizing a window with scrollbars

I have a window with two scrollbars, the scrollbars are repositioned when the window is resized. Inside the WM_PAINT handler, I draw a filled white rectangle just before the scrollbars:

enter image description here

Now, what I assumed will happen when I resize the window is the following:

But when I resize the window vertically, this is what happens:

enter image description here

And when I resize the window horizontally, this is what happens:

enter image description here

This is my code:

#include <Windows.h>

HWND hHorizontalScrollbar;
HWND hVerticalScrollbar;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            // Get width and height of client area for hWnd
            RECT rect;
            GetClientRect(hWnd, &rect);

            // Draw a filled white rectangle just before the scrollbars
            Rectangle(hdc, rect.left, rect.top, rect.right - 17, rect.bottom - 17);

            EndPaint(hWnd, &ps);
        }
        break;
    case WM_SIZE:
        {
            // Get width and height of client area for hWnd
            RECT rect;
            GetClientRect(hWnd, &rect);

            // Change y and width of horizontal scrollbar
            MoveWindow(hHorizontalScrollbar, 0, rect.bottom - 17, rect.right - 220, 17, TRUE);

            // Change x and height of vertical scrollbar
            MoveWindow(hVerticalScrollbar, rect.right - 17, 0, 17, rect.bottom - 220, TRUE);
        }
        break;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "WinClass";
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    RegisterClassEx(&wc);

    HWND hWnd = CreateWindowEx(0, "WinClass", "", WS_OVERLAPPEDWINDOW, 600, 300, 400, 400, NULL, NULL, hInstance, NULL);

    // Create horizontal Scrollbar
    hHorizontalScrollbar = CreateWindowEx(0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE| SBS_HORZ, 0, 333, 300, 17, hWnd, NULL, hInstance, NULL);

    // Create vertical Scrollbar
    hVerticalScrollbar = CreateWindowEx(0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE| SBS_VERT, 333, 0, 17, 300, hWnd, NULL, hInstance, NULL);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

Upvotes: 0

Views: 998

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

You can add InvalidateRect to repaint in WM_SIZE:

case WM_SIZE:
    RECT rect;
    GetClientRect(hWnd, &rect);
    MoveWindow(hHorizontalScrollbar, 0, rect.bottom - 17, rect.right - 220, 17, TRUE);
    MoveWindow(hVerticalScrollbar, rect.right - 17, 0, 17, rect.bottom - 220, TRUE);
    InvalidateRect(hWnd, 0, TRUE); //*** add this

Also, to add scroller, you may not need to create controls, you can just add WS_VSCROLL and WS_HSCROLL flag:

HWND hWnd = CreateWindowEx(0, "WinClass", "", WS_VSCROLL|WS_HSCROLL|WS_OVERLAPPEDWINDOW, 600, 300, 400, 400, NULL, NULL, hInstance, NULL);

Note, if there is a lot of painting done in WM_PAINT then consider overriding WM_ERASEBKGND and break so it doesn't do anything. Do all the background painting in WM_PAINT. You still need InvalidateRect in WM_SIZE

Edit ---------------------------------

When you resize the window, WM_ERASEBKGND is called to update the background. WM_PAINT is called next, however not all WM_PAINT changes are shown on the screen. Windows thinks that there is only a thin line next to the control's edge which needs updating so only that area of the screen is repainted.

As noted by @xMRI, you should set wc.style = CS_HREDRAW | CS_VREDRAW. This would be same as calling InvalidateRect(hWnd, 0, FALSE) in WM_SIZE.

Sometimes you need InvalidateRect(hWnd, 0, TRUE) to force erasing all of the background, but the above example doesn't need full background erase. You can also try WS_CLIPCHILDREN flag to reduce flicker and strange repaint.

Upvotes: 3

Related Questions