使用 WM_DRAWITEM 闪烁
Posted
技术标签:
【中文标题】使用 WM_DRAWITEM 闪烁【英文标题】:Flickering using WM_DRAWITEM 【发布时间】:2013-01-28 20:01:36 【问题描述】:似乎我无法通过所有者绘制控件解决此问题。我已经对状态控件进行了超分类。我正在尝试自定义但仍保留相同的功能。基本上,我想改变背景和文字。我正在使用 Direct2d(或 ID2D1DCRenderTarget 接口)进行绘图。我已经使用 WM_NCPAINT 成功更改了背景;不过,您可以根据需要使用 WM_ERASEBKGRND。然而,这两种方法在我的实验中都充当了对照,并且仍然发生闪烁。此外,当 SB_SETTEXT 的 WPARAM 为 NOT SET 到 SBT_OWNERDRAW 时,不会发生闪烁。因此,我得出一个结论,WM_DRAWITEM 是罪魁祸首。无论如何我可以用所有者绘制状态栏解决这个闪烁问题吗?
【问题讨论】:
我假设这个控件有一个自定义窗口类,对吗? 【参考方案1】:为你的控件开启双缓冲可以避免闪烁。
设置 WS_EX_COMPOSITED 扩展样式: http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543(v=vs.85).aspx
例如处理 WM_CREATE 时,调用(WTL 或 MFC):
ModifyStyleEx(0, WS_EX_COMPOSITED);
【讨论】:
【参考方案2】:嗯,看来我想通了。当对状态栏进行超分类时,请遵循这些设置以避免闪烁。
**注意:这仅在关闭视觉样式的情况下进行了测试。 SetWindowTheme(hWndStatus, L"", L""); 另外,父窗口在创建窗口时必须在样式参数中设置WS_CLIPCHILDREN。
1:覆盖 WM_SIZE。调用 InvalidateRect(m_hWnd, NULL, TRUE) 并返回 0 ,除非您想要默认大小;在这种情况下,调用 CallWindowProc。
2:覆盖 WM_ERASEBKGND 并返回 -1。
3:覆盖 WM_NCPAINT 并将您的绘图代码放在这里。
处理 WM_NCPAINT。人们似乎很难理解如何处理 WM_NCPAINT。这是我的做法。
if (wParam == 1)
hdc = GetWindowDC(m_hWnd);
else
hdc = GetDCEx(m_hWnd, (HRGN) wParam, DCX_WINDOW | DCX_INTERSECTRGN | DCX_CACHE);
然后用 DC 画图。
4:在父过程(WndProc 或其他)中,使用状态栏的句柄调用 SetWindowPos(..., SWP_DRAWFRAME)。这将调整您的状态栏的大小。
5:通过SendMessage(hWndStatusbar, SB_SETPARTS, 1, (LPARAM) &parts)发送消息;
6: 通过 SendMessage(hWndStatusbar, SB_SETTEXT, LOBYTE(0) | SBT_OWNERDRAW, L"Ready") 发送消息。 WM_DRAWITEM 的示例代码:
...
WM_DRAWITEM:
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) lParam;
m_pFramework->m_pD2D1RenderTarget->BindDC(lpDIS->hDC, &lpDIS->rcItem);
m_pFramework->m_pD2D1RenderTarget->BeginDraw();
m_pFramework->m_pD2D1RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::CadetBlue));
D2D1_RECT_F rf = D2D1::RectF(
PixeltoDipX(lpDIS->rcItem.left),
PixeltoDipY(lpDIS->rcItem.top),
PixeltoDipX(lpDIS->rcItem.right),
PixeltoDipY(lpDIS->rcItem.bottom)
);
m_pFramework->m_pD2D1RenderTarget->DrawText(
(LPCWSTR) lpDIS->itemData,
wcslen((WCHAR*) lpDIS->itemData) + 1,
m_pFramework->m_pTextFormat,
rf,
m_d2dCaptionTextColor
);
m_pFramework->m_pD2D1RenderTarget->EndDraw();
break;
....
这应该会停止闪烁。另外,不要在父级的 WM_SIZE 中调用 InvalidateRect(hWndStatus, NULL, TRUE)。这是它闪烁的主要原因。
【讨论】:
以上是关于使用 WM_DRAWITEM 闪烁的主要内容,如果未能解决你的问题,请参考以下文章