How to prevent flicker of CListCtrl?

I use the CListCtrl / CListView ( LVS_REPORT) report view in virtual mode ( LVS_OWNERDATA) with it turned on LVS_EX_DOUBLEBUFFER, and I encounter an ugly flicker. A double buffer has a real effect, but it does not stop all flickering (without it it is very slow).

I am not looking for switching to other controls that require a large number of redistributions (e.g. ObjectListView)

How flicker behaves:

  • when resizing a column - the background is first cleared using lightgray, and after that the text is displayed (the background is white)
  • on the mouse scroll (animated) - for a very short time in the area where new lines will be displayed, the lightgray-bar line is displayed.

It looks like it clears the background using the default background color of the background (lightgray) for the area where it should be redrawn.

How to solve the flicker problem?

+3
source share
2 answers

Try the following: - Install Clip Children and Clip Sibling for the paremt list management dialog. - Make dirived from class CListCtrl. Overwrite OnEraseBkgnd in this class. In OnEraseBkgnd, fill in the background color area around the visible list items. OnEraseBkgnd might look like this:

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
source

I know that the only way to have flicker is to use double buffering or MemDC.

found this article: 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));
}
0

Source: https://habr.com/ru/post/1737894/


All Articles