使用滚动条调整窗口大小时的奇怪效果

时间:2015-09-29 04:35:57

标签: c winapi

我有一个带有两个滚动条的窗口,当调整窗口大小时,滚动条会重新定位。在WM_PAINT处理程序中,我在滚动条之前绘制一个填充的白色矩形:

enter image description here

现在,我在调整窗口大小时会发生以下情况:

  • 首先,发送WM_SIZE消息。在其处理程序内我重新定位 滚动条。
  • 其次,发送WM_PAINT消息。在其处理程序中我重绘 填充的白色矩形。

但是当我垂直调整窗口大小时,就会发生这种情况:

enter image description here

当我水平调整窗口大小时,就会发生这种情况:

enter image description here

这是我的代码:

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

1 个答案:

答案 0 :(得分:3)

您可以在InvalidateRect中重新添​​加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

此外,要添加滚动条,您可能不需要创建控件,只需添加WS_VSCROLLWS_HSCROLL标记:

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

注意,如果在WM_PAINT中完成了大量绘画,那么请考虑覆盖WM_ERASEBKGND并打破,这样就不会做任何事情。在WM_PAINT中完成所有背景画。您仍然需要InvalidateRect

中的WM_SIZE

编辑---------------------------------

调整窗口大小时,会调用WM_ERASEBKGND来更新背景。接下来会调用WM_PAINT,但屏幕上不会显示所有WM_PAINT更改。 Windows认为控件边缘旁边只有一条细线需要更新,因此只重画屏幕的那个区域。

如@xMRI所述,您应该设置wc.style = CS_HREDRAW | CS_VREDRAW。这与在InvalidateRect(hWnd, 0, FALSE)中调用WM_SIZE相同。

有时您需要InvalidateRect(hWnd, 0, TRUE)强制删除所有背景,但上面的示例不需要完全背景擦除。您还可以尝试使用WS_CLIPCHILDREN标志来减少闪烁和奇怪的重绘。