删除和重新添加所有项目和所有列时,列表视图在 Win32 对话框上闪烁

Posted

技术标签:

【中文标题】删除和重新添加所有项目和所有列时,列表视图在 Win32 对话框上闪烁【英文标题】:Listview flickers on Win32 dialog when removing and re-adding all items and all columns 【发布时间】:2010-06-30 14:16:02 【问题描述】:

考虑一个普通的 Win32 对话框,其中包含用 C++ 编写的列表视图控件(在报告模式下)。在某个事件中,所有项目和所有列都将被删除,并创建新的列和项目。基本上,随着内容的变化,会根据内容自动生成列。

当旧项目/列被删除并添加新项目时,列表视图会像地狱一样闪烁。我试过WM_SETREDRAWLockWindowUpdate(),视觉体验没有变化。

我什至设置了扩展列表视图样式LVS_EX_DOUBLEBUFFER,但这根本没有帮助。

父对话框设置了WS_CLIPCHILDREN

有什么建议可以使这项工作尽可能少地闪烁吗?我正在考虑使用两个列表视图,交替可见性,使用隐藏的作为后台缓冲区,但这听起来有点矫枉过正。一定有一个简单的方法。

【问题讨论】:

+1。我也用 VB 列表框看到了这一点。我不确定隐藏的第二个列表框会有所帮助。你怎么知道它什么时候完成闪烁来交换它? 这很简单。在插入项目和列期间发生闪烁。添加最后一项后,闪烁停止。如果将项目和列添加到不可见的列表视图中,则不会发生闪烁。最后一步是显示隐藏的和隐藏可见的列表视图。 【参考方案1】:

默认的列表控件绘制有很大的缺陷。但是有一个简单的技巧可以实现您自己的双缓冲技术:

CMyListCtrl::OnPaint()

    CRect rcClient;
    GetClientRect(rcClient);

    CPaintDC dc(this);
    CDC dcMem;
    dcMem.CreateCompatibleDC(&dc);

    CBitmap bmMem;
    bmMem.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
    CBitmap* pbmOld = dcMem.SelectObject(&bmMem);

    dcMem.FillSolidRect(rcClient, ::GetSysColor(COLOR_WINDOW));

    this->DefWindowProc(WM_PAINT, (WPARAM)dcMem.m_hDC, (LPARAM)0);

    dc.BitBlt(0,0,rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY);
    dcMem.SelectObject(pbmOld);

    CHeaderCtrl*    pCtrl = this->GetHeaderCtrl();
    if (::IsWindow(pCtrl->GetSafeHWnd())
    
        CRect   aHeaderRect;
        pCtrl->GetClientRect(&aHeaderRect);
        pCtrl->RedrawWindow(&aHeaderRect);
    

这将创建一个位图,然后调用默认窗口过程将列表控件绘制到位图中,然后将位图的内容blitting到绘制DC中。

您还应该为 WM_ERASEBKGND 添加一个处理程序:

BOOL CMyListCtrl::OnEraseBkgnd(CDC* pDC)

    return TRUE;

这将阻止控件在重绘之前始终擦除背景。 如果您为位图添加一个成员变量并且仅在窗口大小发生变化时(重新)创建它(因为始终创建位图可能会根据窗口的大小而代价高昂),您可以进一步优化 OnPaint。

这应该很好用。

【讨论】:

我已经使用过很多 ListViews - 没有任何捷径可以让它更好地工作 - 你真的需要加倍缓冲以使其不闪烁。 我在将所有内容从 MFC 转换为普通 Win32 后尝试过这个,但它只能部分工作。我有两个问题。次要的一个是水平滚动条随着列的删除和添加而闪烁。但我可以忍受这一点,因为大多数闪烁问题都已解决。第二个主要问题是调整列大小时列表视图不重新绘制。我想正确的方法是听取足够的通知并手动重新绘制。 这个技巧唯一应该闪烁的是标题控件。您是否可能将自己的标题子类化,这可能会导致重绘问题? 不,我只是将列表视图子类化并按照您的建议处理WM_ERASEBKGNDWM_PAINT。我尝试处理HDN_TRACK,以便我可以重新绘制,但它永远不会由标题控件发送。有趣的是,即使没有 WM_ERASEBKGNDWM_PAINT 处理程序,仅子类化列表视图就会停止重新绘制标题调整大小。 很抱歉给您带来了困惑。我的代码在另一个地方有问题。【参考方案2】:

在尝试了很多事情以及最重要的是humbagumba 的建议之后,我得出了一个非常简单的结论。 LockWindowUpdate 在这种情况下是每个人的朋友。我不知道为什么它第一次对我不起作用,但是在自定义绘画在所有情况下都无法交付之后,我再次尝试 LockWindowUpdate 并且它起作用了!

基本上,只需将 listview 上的所有工作都包含在 LockWindowUpdate(hWnd)LockWindowUpdate(NULL) 中,一切都会很好地工作。甚至没有滚动条闪烁了。

请确保不要嵌套LockWindowUpdate,因为一次只能锁定一个窗口。

【讨论】:

除非用户用鼠标拖动某些东西,否则 LockWindowUpdate 不是你的朋友,请参阅 blogs.msdn.com/b/oldnewthing/archive/2007/02/21/1735472.aspx 和 blogs.msdn.com/b/oldnewthing/archive/2007/02/22/1742084.aspx

以上是关于删除和重新添加所有项目和所有列时,列表视图在 Win32 对话框上闪烁的主要内容,如果未能解决你的问题,请参考以下文章

如何根据优先级重新排列列表视图中的项目?

列表视图显示所有项目减去最后一个

Sonata Admin + 在列表视图中显示所有项目(不是每页)

在 Mysql 中添加新列时如何轻松维护审计触发器

如何添加多个视图和删除视图并在删除后从视图中获取所有数据?

Sharepoint Online / 365 - 从列表视图中删除多个附加列和显示条目的“查看条目”