65.9K
CodeProject is changing. Read more.
Home

FLICKER-free

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.33/5 (26 votes)

Apr 23, 2003

CPOL

1 min read

viewsIcon

127620

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. Cabnet - usging the list control without
flickering

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 saving

If 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.