winapi - 启用 LWA_COLORKEY 时如何防止颜色键明显闪烁
问题描述
我有一扇窗户,有时应该有一个透明的洞,有时没有。理想情况下,我们会使用 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;
}
解决方案
我不认为分层窗口设计为每秒打开和关闭多次(每次切换时,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;
推荐阅读
- typescript - 我如何重新编码此函数以实际等待结果
- python-3.x - 与示例句子列表相比,如何提取具有相似含义/意图的句子
- javascript - 异步 Javascript 函数在 IE 11 中不起作用
- java - 如何使用具有通用数据类型的 printf/formatter 在 Java 和 POI 中的控制台中制作表格
- javascript - 以数字方式重新排序创建的 div
- r - 有没有办法只突出显示 RStudio 文本字符串中文本的某些部分?
- git - 将每个 git commit 转换为单独的文件夹
- google-sheets - 为 Google 表格中的 2 列生成所有可能的组合
- ruby-on-rails - Capybara/Rspec - 有没有办法在点击提交之前测试输入框是否已填写?
- java - Selenium - Chrome 中的拖放问题