문제

Consider a plain Win32 dialog with listview control (in report mode) written in C++. Upon a certain event all items and all columns are deleted and new columns and items are created. Basically, as content changes, columns are automatically generated based on content.

When old items/columns are removed and new ones added, listview flickers like hell. I have tried WM_SETREDRAW and LockWindowUpdate() with no change to visual experience.

I have even set extended listview style LVS_EX_DOUBLEBUFFER and that didn't help at all.

The parent dialog has WS_CLIPCHILDREN set.

Any suggestions how to make this work with as little flicker as possible? I am thinking of using two listviews, alternating visibility, using the hidden one as a back buffer but this sounds like an overkill. There must be an easy way.

도움이 되었습니까?

해결책

The default list control painting is pretty flawed. But there is a simple trick to implement your own double-buffering technique:

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);
    }
}

This will create a bitmap and then call the default window procedure to paint the list control into the bitmap and then blitting the contents of the bitmap into the paint DC.

You should also add a handler for WM_ERASEBKGND:

BOOL CMyListCtrl::OnEraseBkgnd(CDC* pDC)
{
    return TRUE;
}

This will stop the control from always erasing the background before a redraw. You can optimize the OnPaint further if you add a member variable for the bitmap and only (re)create it when the size of the window changed (because always creating a bitmap may be costly depending on the size of the window).

This should work pretty well.

다른 팁

After trying many things and most of all humbagumba's suggestions I have come to a very simple conclusion. LockWindowUpdate is everybody's friend in this sort of situation. I am not sure how come it failed to work for me the first time but after custom painting failed to deliver in all situations I have tried LockWindowUpdate once again and it worked!

Basically, just wrap all work on listview in a LockWindowUpdate(hWnd) and LockWindowUpdate(NULL) and things work beautifully. There is not even a scrollbar flicker any more.

Just make sure not to nest LockWindowUpdate as only one window can be locked at a time.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top