使用 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 闪烁的主要内容,如果未能解决你的问题,请参考以下文章

MFC自绘Button按钮分析和实现

MFC GUI自定义控件:如何绘制光标更新以响应鼠标移动?

Ownerdraw按钮的悬停效果

控件自绘DRAWITEMSTRUCT

用句柄操作下拉框

自绘制菜单带图标