当样式为 WS_CHILD 时,通过 ps.rcPaint 查找脏区不起作用

Posted

技术标签:

【中文标题】当样式为 WS_CHILD 时,通过 ps.rcPaint 查找脏区不起作用【英文标题】:Finding dirty region by ps.rcPaint not works when style is WS_CHILD 【发布时间】:2015-11-28 20:27:39 【问题描述】:

我用C++写过用WinApi画的程序。 我的回调函数:

/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

    switch (message)                  /* handle the messages */
    
        case WM_DESTROY:
            PostQuitMessage(0);       /* send a WM_QUIT to the message queue */
            break;
        case WM_ERASEBKGND:
            
                elWidget *widget = (elWidget *)GetWindowLong(hwnd, GWL_USERDATA);
                if (widget)
                
                    PAINTSTRUCT ps;
                    HDC hdc = BeginPaint(hwnd, &ps);
                    HBRUSH hBrush = CreateSolidBrush(widget->color.ColorRef());
                    FillRect((HDC)wParam, &ps.rcPaint, hBrush);
                    DeleteObject(hBrush);
                    EndPaint(hwnd, &ps);
                
            
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc(hwnd, message, wParam, lParam);
    
    return 0;

它适用于独立窗口(样式为WS_OVERLAPPED),但当样式为WS_CHILDWS_CHILD | WS_VISIBLE 时,ps.rcPaint 始终为 (0,0,0,0)。不知道怎么解决。

elButton::elButton(elWidget *owner)
        : elWidget(owner)

    WNDCLASSEX winclChild;        /* Data structure for the windowclass */
    /* The Window structure */
    winclChild.hInstance = gThisInstance; //global variable instance
    winclChild.lpszClassName = L"Child";
    winclChild.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    winclChild.style = CS_DBLCLKS;                 /* Catch double-clicks */
    winclChild.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    winclChild.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    winclChild.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    winclChild.hCursor = LoadCursor (NULL, IDC_ARROW);
    winclChild.lpszMenuName = NULL;                 /* No menu */
    winclChild.cbClsExtra = 0;                      /* No extra bytes after the window class */
    winclChild.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    winclChild.hbrBackground = 0;// CreateSolidBrush(RGB(255, 200, 200));//(HBRUSH)COLOR_WINDOW;//COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx(&winclChild))
        return;
    hwnd = CreateWindowEx(
        0,                   /* Extended possibilites for variation */
        L"Child",         /* Classname */
        L"Title",       /* Title Text */
        WS_CHILD | WS_VISIBLE,
        100,
        100,
        40,
        40,
        owner->getHwnd(),        /* The window is a child-window to desktop */
        NULL,                /* No menu */
        gThisInstance,       /* Program Instance handler */
        this                 /* to lParam */
       );
    SetWindowLong(hwnd, GWL_USERDATA, (long)this);

我可以在 Google Disk 上添加指向整个项目的链接,但我不能保证它会永久保存多年。

【问题讨论】:

随机猜测是您忘记设置 CS_VREDRAW | CS_HREDRAW 类样式标志。您需要显示调用 RegisterWindow/Ex() 和 CreateWindow/Ex() 的代码。 WNDCLASSEX winclChild; winclChild.style = CS_DBLCLKS if (!RegisterClassEx(&winclChild)) 返回; hwnd = CreateWindowEx(WS_CHILD | WS_VISIBLE, 显示minimal reproducible example 这样我们就不必以这种方式从您那里提取代码不是更好吗? 第一个答案:但两个窗口都是红色的;如果我们应用“unsigned style = GetWindowLong(hWnd, GWL_STYLE); if ((style & WS_CHILD) != 0) FillRect((HDC)wParam, &ps.rcPaint, hBrush);”两者都是白色的,否则是红色的 @Saku:我已经更新了我的答案。 【参考方案1】:

BeginPaint 只能在 WM_PAINT 上调用,

要获取 WM_ERASEBKGND 上的剪切框,请改为调用 GetClipBox((HDC)wParam,&rect);

【讨论】:

【参考方案2】:

仅将BeginPaint/EndPaint 用于WM_PAINT

来自WM_PAINT: 的 Windows 文档

应用程序不应调用 BeginPaint,除非响应 WM_PAINT 消息。对BeginPaint 的每次调用都必须有一个 对应调用EndPaint函数。

虽然您的代码看起来可以工作,但它可能会在其他地方引起问题,例如WM_PAINT

要从WM_PAINT 外部获取更新区域,请使用GetUpdateRect。但是,GetUpdateRect 不能用于WM_ERASEBKGND。另见GetUpdateRect

一个简单的解决方案是强制WM_ERASEBKGND 什么都不做,然后在WM_PAINT 中处理所有事情

switch(msg)

case WM_ERASEBKGND:
    return TRUE;
case WM_PAINT:
    ...

【讨论】:

是的,但我更喜欢只重绘脏部分;【参考方案3】:

这段代码非常适合我。答案是我们必须分别为父窗口和子窗口定义单独的窗口类。我看到SetWindowLong 在所有情况下都能正常工作。但是如果你想立即用新数据重绘新窗口,你必须删除WS_VISIBLE作为你的创建样式,并在ShowWindow之前删除SetWindowLongShowWindow 函数使您的窗口可见并使用新参数重新绘制它。

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,
                         LPARAM lParam);

int RegClass(HINSTANCE hInstance,LPCTSTR lpszClassName)

    WNDCLASS wc;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = (LPCTSTR)lpszClassName;
    wc.lpszMenuName = (LPCTSTR)NULL;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wc.hCursor = (HCURSOR)LoadCursor(NULL,IDC_CROSS);
    wc.hIcon = (HICON)LoadIcon(NULL,IDI_APPLICATION);
    return RegisterClass(&wc);


HWND hWnd0, hWnd1;

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd)

    MSG message;

    static LPCTSTR lpszClName = L"Wnd Class";
    static LPCTSTR lpszCl0Name = L"Child Class";
    if(!RegClass(hInstance,lpszClName))
        exit(0);
    if(!RegClass(hInstance,lpszCl0Name))
        exit(0);

    hWnd0 = CreateWindow(
        lpszClName,
        L"Привіт!",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        800,
        600,
        0,
        0,
        hInstance,
        NULL
        );

    hWnd1 = CreateWindow(
        lpszCl0Name,
        L"Привіт!",
        WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
        40,
        40,
        400,
        300,
        hWnd0,
        0,
        hInstance,
        NULL
        );

    ShowWindow(hWnd0,SW_SHOWNORMAL);
    UpdateWindow(hWnd0);

    RECT WndRect;

    // For parent redraw
    GetWindowRect(hWnd0, &WndRect);
    MoveWindow(hWnd0, WndRect.left, WndRect.top, 801, 601, TRUE);

    SetWindowLong(hWnd0, GWL_USERDATA, RGB(0, 255, 0));

    ShowWindow(hWnd1,SW_SHOWNORMAL);
    UpdateWindow(hWnd1);

    while (GetMessage(&message,0,0,0))
    
        TranslateMessage(&message);
        DispatchMessage(&message);
    

    return message.wParam;


LRESULT CALLBACK WndProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,
                         LPARAM lParam)

    switch (message)                  /* handle the messages */
    
        case WM_CREATE:
            InvalidateRect(hWnd, NULL, TRUE);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);       /* send a WM_QUIT to the message queue */
            break;
        case WM_ERASEBKGND:
            
                if (true)
                
                    PAINTSTRUCT ps;
                    HDC hdc = BeginPaint(hWnd, &ps);

                    COLORREF color = (hWnd == hWnd0) ? RGB(255, 0, 0) : GetWindowLong(hWnd0, GWL_USERDATA);

                    HBRUSH hBrush = CreateSolidBrush(color);
                    FillRect((HDC)wParam, &ps.rcPaint, hBrush);
                    DeleteObject(hBrush);
                    EndPaint(hWnd, &ps);

                    ValidateRect(hWnd, NULL);
                
            
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc(hWnd, message, wParam, lParam);
    
    return 0;

结果会是。

【讨论】:

但要解决一个问题:类的窗口可以有不同的颜色,但无法将窗口与有颜色的对象关联起来。在 CreateWindow 或 CreateWindowEx 之前 hWnd 的值是未知的。 CreateWindow 之后已经调用了 WndProc,尤其是对于 WS_CHILD。没有 BeginUpdate 不好,但是在设置 SetWindowLong(hwnd, GWL_USERDATA, (long)this) 或放入 Map 之前立即运行擦除背景。这是个问题。 @Saku:你写的不是很清楚,我根本无法理解这个问题。你能试着更清楚地写出你想要达到的目标吗?可以分步执行吗? @Saku:你有没有试图告诉我没有办法向未创建的窗口发送消息? 解决方案:不应该是 WS_VISIBLE 样式,因为 WndProc 在 SetWindowLong 之前运行

以上是关于当样式为 WS_CHILD 时,通过 ps.rcPaint 查找脏区不起作用的主要内容,如果未能解决你的问题,请参考以下文章

将 WS_CHILD 替换为 WS_POPUP 后,CWnd::CreateEx() 失败

窗口与窗口之间的关系

仅当样式设置为“滚动”时 UIPageViewController 中的约束异常

在使用Towify制作小程序时,如何配置样式的选中激活?

当网络字体加载缓慢时恢复为无样式文本?

仅当没有 h1 同级元素时才为元素设置样式