Abstract
Article is on a reusable class inherited from CScrollView
to implement buffered scrolling.
Objective
Although "double buffered drawing" is a common technique that every intermediate programmer knows, tt�s quite tricky to implement a smoothly no flicker scrolling view. What if an application already drawing directly on a CScrollView
wants to use the technique? Does it need to change much code? No. All those tricky things can be concealed in one reusable class. All you need is to inherit your class from CCachedScrollView
instead of CScrollView
.
Implementation
- First Step: Create a class
CCachedScrollView
inherited from CScrollView
using the MFC class wizard.
- Second Step: Add member fields
CacheBitmap
that is the memory bitmap, CacheValid
and CacheRect
for scroll buffer. At last, process the WM_PAINT
message by OnPaint
.
CachedScrollView.h
class CCachedScrollView : public CScrollView
{
DECLARE_DYNCREATE(CCachedScrollView)
protected:
CCachedScrollView();
virtual ~CCachedScrollView();
virtual void OnDraw(CDC* pDC) { }
private:
CBitmap CacheBitmap;
public:
BOOL CacheValid;
CRect CacheRect;
void SetCacheSize(SIZE sz) {
if (CacheRect.IsRectNull())
CacheRect=CRect(CPoint(0,0),sz);
} DECLARE_MESSAGE_MAP()
afx_msg void OnPaint();
};
CachedScrollView.cpp
void CCachedScrollView::OnPaint()
{
CPaintDC dc(this);
OnPrepareDC(&dc);
if (CacheRect.IsRectNull())
{
OnDraw(&dc);
return;
}
if (CacheBitmap.m_hObject==NULL)
CacheBitmap.CreateCompatibleBitmap(&dc,CacheRect.Width(),
CacheRect.Height());
CRect ClientRect;
GetClientRect(ClientRect);
CPoint org=dc.GetViewportOrg();
ClientRect.OffsetRect(-org.x,-org.y);
if (!CacheRect.PtInRect(ClientRect.TopLeft()) ||
!CacheRect.PtInRect(ClientRect.BottomRight()))
{
CacheRect.OffsetRect(
ClientRect.CenterPoint()-CacheRect.CenterPoint());
CacheValid=FALSE;
}
CDC mdc;
mdc.CreateCompatibleDC(&dc);
mdc.SelectObject(&CacheBitmap);
if (!CacheValid) {
mdc.SelectStockObject(WHITE_BRUSH);
mdc.SelectStockObject(NULL_PEN);
mdc.Rectangle(0,0,CacheRect.Width(),CacheRect.Height());
mdc.SelectStockObject(BLACK_PEN);
mdc.SetViewportOrg(-CacheRect.left,-CacheRect.top);
OnDraw(&mdc);
mdc.SetViewportOrg(0,0);
CacheValid=TRUE;
}
dc.SelectClipRgn(NULL);
dc.BitBlt(CacheRect.left,CacheRect.top,
CacheRect.Width(),CacheRect.Height(),&mdc,0,0,SRCCOPY);
}
Usage
You can write your own view inherited from CCachedScrollView
, pay attention to call
the SetCacheSize()
method in InitUpdate()
for setting the buffer image size.