FLICKER-free






1.33/5 (26 votes)
how to implement double buffer in the list cnntrol to avoid flipping
Introduction
This is the main window of the cabnet project.As you see - the list control contains the driver's numbers identification - placed time sortly.
While usually the MFC technique to draw the list controls items is sufficient, i had confronted a crises with my last project. the drawing operations inside the internal OnPaint function was truely heavy, And the view was flickly disaster.
Conclusion : don't take the flicking - flippancy !!! To avoid flicking in the list control you have to create your own extention to the original CListCtrl class and to implement the following functions.
class CListCtrlEx : public CListCtrl { protected: void DrawItem(LPDRAWITEMSTRUCT); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnPaint(); public: void SetCustomFont(int nSize,LPCSTR szName) protected : TEXTMETRIC m_fnt_tm; CFont m_fntCustom; ... ... DECLARE_MESSAGE_MAP() }
You could choose a custom font :
void CListCtrlEx::SetCustomFont(int nSize,LPCSTR szName) { if (m_fntCustom,GetSafeHandle()) m_fntCustom.DestroyFont(); m_fntCustom.CreateFont(nSize, 0, 0, 0, 400, FALSE, FALSE, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS,szName); SetFontHeight() }
void CListCtrlEx::SetFontHeight() { CClientDC dc(this); // select font or stay with the default font CFont *pFontOld = NULL; if (m_fntCustom.GetSafeHandle()) pFontOld = dc.SelectObject(&m_fntCustom); TEXTMETRIC tm; ::GetTextMetrics(dc.m_hDC,&tm); m_fnt_tm = tm; // drop font if (pFontOld) dc.SelectObject(pFontOld); }
BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC) { return 1; //return CListCtrl::OnEraseBkgnd(pDC); // this is the criminal // who make the most flicking problems... }
void CListCtrlEx::OnPaint() { CPaintDC dc(this); // device context for painting int nVertPos; CDC dcm; CRect rc; GetClientRect(rc); dcm.CreateCompatibleDC(&dc); CBitmap bmt; bmt.CreateCompatibleBitmap(&dc,rc.Width(),rc.Height()); CBitmap *pBitmapOld = dcm.SelectObject(&bmt); dcm.Rectangle(rc);// make the work of the OnEraseBkgnd function DRAWITEMSTRUCT dd; dd.hwndItem = m_hWnd; dd.hDC = dc.m_hDC; dc.SetBkMode(TRANSPARENT); // select objects as && if you like nVertPos = GetScrollPos(SB_VERT); if (nVertPos!=0) { nVertPos = nVertPos/m_fnt_tm.tmHeight; } for (int v=0; v<rc.Height()/m_fnt_tm.tmHeight; v++) { dd.itemID = v+nVertPos; DrawItem(&dd); } BitBlt(dc.m_hDC,0,0,rc.Width(),rc.Height(),dcm.m_hDC,0,0,SRCCOPY); dcm.SelectObject(pBitmapOld); // drop other objects from the dc context memory - if there are. }
void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT ld) { CHeaderCtrl* pHeader = GetHeaderCtrl( ); int nCols = pHeader->GetItemCount( ); CRect rf; int nVertPos = GetScrollPos(SB_VERT); for (int nIndex=0; nIndex<nCols; nIndex++) { GetSubItemRect( ld->itemID, nIndex,LVIR_BOUNDS , rf); rf.top -= nVertPos; rf.bottom = rf.top + m_fnt_tm.tmHeight; // draw what ever you like on the rf rectangle... } }Finally, you have to create the list control with the LVS_OWNERDRAWFIXED style.
note : if you decide to choose custom font you have also to implement the GetSubItemRect function because the re-paint rectangle should be in regard to the custom font size.
BOOL CListCtrlEx::GetSubItemRect(int iItem, int iSubItem, int nArea, CRect &ref) { BOOL bRet = CListCtrl::GetSubItemRect( iItem, iSubItem, nArea, ref ); if (!m_fntCustom.GetSafeHandle()) return bRet; ref.top = (iItem*m_fnt_tm.tmHeight); ref.bottom=ref.top+m_fnt_tm.tmHeight; return bRet; }p.s : If the list control has the WS_HSCROLL style - you have to adjust the drawing the same way we used for the WS_VSCROLL case - by using the GetScrollPos() function with the value SB_HORZ for its argument.
tip
bitmap store for speedness and memory savingIf you have multiple windows to draw by yourself - consider to use big CBitmap variable stored in stack or in the heap for the life of the app, and take it as your memory storeroom for all your painting.