【问题标题】:How do I prevent flickering on CListCtrl?如何防止 CListCtrl 上的闪烁?
【发布时间】:2011-01-30 00:14:46
【问题描述】:

我在启用LVS_EX_DOUBLEBUFFER 的虚拟模式(LVS_OWNERDATA) 下使用CListCtrl/CListView 报告视图(LVS_REPORT),但遇到难看的闪烁。双缓冲有一个真正的效果,但它不会停止所有闪烁(没有它非常慢)。

我不想切换到需要大量返工的其他控件(如 ObjectListView)

闪烁是如何表现的:

  • 在调整列大小时 - 首先使用浅灰色清洁背景,然后显示文本(背景为白色)
  • 鼠标滚动(动画) - 在很短的时间内,在要显示新行的区域中会显示浅灰色条。

看起来它确实使用默认窗口背景颜色(浅灰色)清理了必须重绘区域的背景。

如何解决闪烁问题?

【问题讨论】:

    标签: c++ windows mfc clistctrl common-controls


    【解决方案1】:

    尝试执行以下操作: - 为 List Control 的 paremt 对话框设置 Clip Children 和 Clip Sibling。 - 由 CListCtrl 类驱动。在此类中覆盖 OnEraseBkgnd。在 OnEraseBkgnd 中填充列表可见项目周围的背景颜色区域。 OnEraseBkgnd 可能如下所示:

    BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC) 
    {
        CBrush  br;
        CRect   rcCli;
        CRect   rcItemsRect(0, 0, 0, 0);
        int     nHeadHeight = 0;
        int     nItems      = GetItemCount();
    
        GetClientRect(&rcCli);
    
        CHeaderCtrl* pHeadCtrl = GetHeaderCtrl();
        if (pHeadCtrl)
        {
            CRect  rcHead;
            pHeadCtrl->GetWindowRect(&rcHead);
            nHeadHeight = rcHead.Height();
        }
        rcCli.top += nHeadHeight;
    
        if (nItems > 0)
        {
            CPoint  ptItem;
            CRect   rcItem;
    
            GetItemRect(nItems - 1, &rcItem, LVIR_BOUNDS);
            GetItemPosition(nItems - 1, &ptItem);
    
            rcItemsRect.top    = rcCli.top;
            rcItemsRect.left   = ptItem.x;
            rcItemsRect.right  = rcItem.right;
            rcItemsRect.bottom = rcItem.bottom;
    
            if (GetExtendedStyle() & LVS_EX_CHECKBOXES)
                rcItemsRect.left -= GetSystemMetrics(SM_CXEDGE) + 16;
        }
    
        br.CreateSolidBrush(GetBkColor());
    
        if (rcItemsRect.IsRectEmpty())
            pDC->FillRect(rcCli, &br);
        else
        {
            if (rcItemsRect.left > rcCli.left)     // fill left rectangle
                pDC->FillRect(
                    CRect(0, rcCli.top, rcItemsRect.left, rcCli.bottom), &br);
            if (rcItemsRect.bottom < rcCli.bottom) // fill bottom rectangle
                pDC->FillRect(
                    CRect(0, rcItemsRect.bottom, rcCli.right, rcCli.bottom), &br);
            if (rcItemsRect.right < rcCli.right) // fill right rectangle
                pDC->FillRect(
                    CRect(rcItemsRect.right, rcCli.top, rcCli.right, rcCli.bottom), &br);
        }
    
        return TRUE;
    }
    

    【讨论】:

      【解决方案2】:

      我知道没有闪烁的唯一方法是使用双缓冲或 MemDC。

      找到这篇文章:Flicker-free-drawing-of-any-control

      这篇文章很好地解释了如何在你的 CListCtrl 上快速执行非闪烁绘图。 而且效果很好。

      PS:VS 2005 没有 CMemDC 类,你需要自己实现它,或者使用以下代码:

      //
      // CMemDC.h header file
      //
      #pragma once
      
      class CMemDC
      {
      public:
          CMemDC(CDC& dc, CWnd* pWnd);
          CMemDC(CDC& dc, const CRect& rect);
      
          virtual ~CMemDC();
      
          CDC& GetDC() { return m_bMemDC ? m_dcMem : m_dc; }
          BOOL IsMemDC() const { return m_bMemDC; }
          BOOL IsVistaDC() const { return m_hBufferedPaint != NULL; }
      
        void EraseBkClip();
      protected:
          CDC&     m_dc;
          BOOL     m_bMemDC;
          HANDLE   m_hBufferedPaint;
          CDC      m_dcMem;
          CBitmap  m_bmp;
          CBitmap* m_pOldBmp;
          CRect    m_rect;
      };
      
      //
      // CMemDC.cpp source file
      //
      #include "CMemDC.h"
      
      CMemDC::CMemDC(CDC& dc, CWnd* pWnd) :
          m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL)
      {
          ASSERT_VALID(pWnd);
      
          pWnd->GetClientRect(m_rect);
          m_rect.right += pWnd->GetScrollPos(SB_HORZ);
          m_rect.bottom += pWnd->GetScrollPos(SB_VERT);   
          if (m_dcMem.CreateCompatibleDC(&m_dc) && 
            m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
          {
              m_bMemDC = TRUE;
              m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
          }
      }
      
      CMemDC::CMemDC(CDC& dc, const CRect& rect) :
          m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL), m_rect(rect)
      {
          ASSERT(!m_rect.IsRectEmpty());
          if (m_dcMem.CreateCompatibleDC(&m_dc) &&
            m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
          {
              m_bMemDC = TRUE;
              m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
          }
      }
      
      CMemDC::~CMemDC()
      {
          if (m_bMemDC)
          {
              CRect rectClip;
              int nClipType = m_dc.GetClipBox(rectClip);
      
              if (nClipType != NULLREGION)
              {
                  if (nClipType != SIMPLEREGION)
                  {
                      rectClip = m_rect;
                  }
                  m_dc.BitBlt(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(), &m_dcMem, rectClip.left, rectClip.top, SRCCOPY);
              }
      
              m_dcMem.SelectObject(m_pOldBmp);
          }
      }
      
      void CMemDC::EraseBkClip()
      {
        CRect clip;
        m_dcMem.GetClipBox(&clip);
        m_dcMem.FillSolidRect(clip, GetSysColor(COLOR_WINDOW));
      }
      

      【讨论】:

        【解决方案3】:

        我发现有一种非常简单的方法对我有用:

        1. 使用m_List1.SetRedraw(false) 关闭重绘
        2. m_List1.ResetContents()重置内容
        3. 使用m_List1.AddString() 在循环中添加新字符串
        4. 然后通过重新启用重绘和m_List1.UpdateWindow() 来完成。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-01-06
          • 1970-01-01
          • 2010-11-28
          • 2015-10-18
          • 1970-01-01
          • 1970-01-01
          • 2020-05-07
          • 2012-02-03
          相关资源
          最近更新 更多