1

我有一扇窗户,有时应该有一个透明的洞,有时没有。理想情况下,我们会使用 SetWindowRgn,但这会禁用视觉样式,这不仅看起来很难看,而且无法正确绘制每个监视器的 DPI 感知,因此我尝试使用带有颜色键的分层窗口。

启用颜色键时,我首先调用SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY),然后使窗口无效以便重绘。此时窗口不应包含关键颜色。然后窗口WM_PAINT在稍后的某个时间接收,并且绘制了键颜色,但是此时窗口应该已经LWA_COLORKEY设置,所以我再次希望键颜色不可见。

禁用颜色键时,我首先(同步)重新绘制窗口,使其不包含键颜色,然后禁用WS_EX_LAYERED,所以我从没想过会看到键颜色。

但是,当鼠标在其上移动时,具有以下窗口过程的窗口会在绿色、透明和背景颜色之间不断闪烁。

似乎可能SetLayeredWindowAttributes不会立即生效(甚至在下一个之前WM_PAINT)。如何确保此属性在重新绘制之前已生效,或以其他方式阻止关键颜色可见?

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static auto const colorkey = RGB(0,255,0);
    static auto const hbrush = CreateSolidBrush(colorkey);
    static auto transparent = false;
    switch (message)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            if (transparent) {
                RECT rect{30,30,500,500};
                FillRect(hdc, &rect, hbrush);
            }
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_MOUSEMOVE:
        if (transparent) {
            transparent = false;
            RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
            SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
        } else {
            SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
            SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
            transparent = true;
            InvalidateRect(hWnd, nullptr, TRUE);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
4

2 回答 2

0

我不认为分层窗口设计为每秒打开和关闭多次(每次切换时,Windows 都会分配/销毁 32 BPP 图像等)。

SWP_FRAMECHANGED至少对我来说,额外的擦除确实让它变得更好:

case WM_MOUSEMOVE:
    if (transparent) {
        transparent = false;
        RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INTERNALPAINT|RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        InvalidateRect(hWnd, nullptr, true);
    } else {
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        transparent = true;
        InvalidateRect(hWnd, nullptr, true);
    }
    TCHAR b[99];wsprintf(b,TEXT("%d tick=%d"), transparent, GetTickCount()), SetWindowText(hWnd, b);
    break;
于 2019-07-05T17:42:32.907 回答
0

似乎那些分层的窗口变化需要一些时间才能反映在窗口的渲染中。添加一个睡眠可以使绿色不显示。

case WM_MOUSEMOVE:
    if (transparent) {
        transparent = false;
        RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
    }
    else {
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
        Sleep(1); // Add sleep
        transparent = true;
        InvalidateRect(hWnd, nullptr, TRUE);
    }
    break;
于 2019-07-29T09:27:19.803 回答