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

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

最佳答案

您可以添加 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 中完成所有背景绘制。您仍然需要 WM_SIZE

中的 InvalidateRect

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

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

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

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

关于c - 使用滚动条调整窗口大小时出现奇怪的效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32835771/

相关文章:

java - 用于列出源文件中所有函数的 linux 工具?

c++ - 为什么我的子窗口对鼠标事件没有反应?

c++ - 哪个版本的 Windows SDK 使用 xinput1_3.dll?

c# - 来自 user32.dll 的 FindWindowEx 使用 dllimport 返回零句柄和 127 错误代码

c - 如何更改 C 函数中传递给函数的指针所指向的内容?

c - 无法解释 printf 输出

java - 调用由 SWIG 生成的 JNI 的 UnsatisfiedLinkError?

c - 使用 printf 格式说明符为不正确的符号生成警告

winapi - 实现类似语音服务器的teamspeak

xml - 如何在 .propdesc 文件中使用 relatedProperty