Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Custom CStatusBar and double-buffering

74 views
Skip to first unread message

Bruce L

unread,
May 5, 2008, 8:54:46 PM5/5/08
to
Hi,

Wondering if anyone has any good ideas here...

I have written a custom CStatusBar control which basically updates
some panes in color as the mouse cursor is moved around. The panes
were flickering due to the amount of redrawing required so I added
some code that uses double-buffering to draw the panes off-screen
first, then blits to screen. I used a well-known piece of code from
Keith Rule on CodeProject in the CMemDC class for this (http://
www.codeproject.com/KB/GDI/flickerfree.aspx?msg=2531592#xx2531592xx).
Here's the code:

<code>
class CMemDC : public CDC {
private:
CBitmap m_bitmap; // Offscreen bitmap
CBitmap* m_oldBitmap; // bitmap originally found in CMemDC
CDC* m_pDC; // Saves CDC passed in constructor
CRect m_rect; // Rectangle of drawing area.
BOOL m_bMemDC; // TRUE if CDC really is a Memory DC.
public:

CMemDC(CDC* pDC, const CRect* pRect = NULL) : CDC()
{
ASSERT(pDC != NULL);

// Some initialization
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC = !pDC->IsPrinting();

// Get the rectangle to draw
if (pRect == NULL) {
pDC->GetClipBox(&m_rect);
} else {
m_rect = *pRect;
}

if (m_bMemDC) {
// Create a Memory DC
CreateCompatibleDC(pDC);
pDC->LPtoDP(&m_rect);

m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(),
m_rect.Height());
m_oldBitmap = SelectObject(&m_bitmap);

SetMapMode(pDC->GetMapMode());

SetWindowExt(pDC->GetWindowExt());
SetViewportExt(pDC->GetViewportExt());

pDC->DPtoLP(&m_rect);
SetWindowOrg(m_rect.left, m_rect.top);
} else {
// Make a copy of the relevent parts of the current DC for printing
m_bPrinting = pDC->m_bPrinting;
m_hDC = pDC->m_hDC;
m_hAttribDC = pDC->m_hAttribDC;
}

// Fill background
FillSolidRect(m_rect, pDC->GetBkColor());
}

~CMemDC()
{
if (m_bMemDC) {
// Copy the offscreen bitmap onto the screen.
m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(),
m_rect.Height(),
this, m_rect.left, m_rect.top, SRCCOPY);

//Swap back the original bitmap.
SelectObject(m_oldBitmap);
} else {
// All we need to do is replace the DC with an illegal value,
// this keeps us from accidently deleting the handles associated
with
// the CDC that was passed to the constructor.
m_hDC = m_hAttribDC = NULL;
}
}

// Allow usage as a pointer
CMemDC* operator->()
{
return this;
}

// Allow usage as a pointer
operator CMemDC*()
{
return this;
}
};
</code>

Applying this code requires overriding the OnPaint & OnEraseBkgnd
methods in the custom CStatusBar as follows:

<code>
void MyStatusBar::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CStatusBar::OnPaint() for painting messages
CRect rect;
GetClientRect(&rect);
CMemDC memDC(&dc, &rect);

DefWindowProc(WM_PAINT, (WPARAM)memDC->m_hDC, (LPARAM)0);
}

BOOL MyStatusBar::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
//return CStatusBar::OnEraseBkgnd(pDC);
return TRUE;

}
</code>

This reduces the flickering, but now the pane borders don't get drawn
at all - they just appear as blank white areas. I have tried
overriding the OnNcPaint function to avoid the call to
CControlBar::EraseNonClient(). No effect. I have also tried calling
DrawBorders explicitly with likewise no effect.

Does anyone have any idea what I'm missing here?

Any suggestions would be much appreciated.

Best,
Bruce

Joseph M. Newcomer

unread,
May 5, 2008, 11:10:49 PM5/5/08
to
See below...

****
Just note that calling DefWindowProc like this is essentially the same as calling
CStatusBar::OnPaint, if you follow the logic.

I would be inclined to use the virtual DrawItem method; see my essay on www.codeguru.com
(I posted it there a few months before they were bought out and lost their glitter);
there's also a link to this from my MVP Tips site (unless they moved the article)
joe
****


>}
>
>BOOL MyStatusBar::OnEraseBkgnd(CDC* pDC)
>{
> // TODO: Add your message handler code here and/or call default
> //return CStatusBar::OnEraseBkgnd(pDC);
> return TRUE;
>
>}
></code>
>
>This reduces the flickering, but now the pane borders don't get drawn
>at all - they just appear as blank white areas. I have tried
>overriding the OnNcPaint function to avoid the call to
>CControlBar::EraseNonClient(). No effect. I have also tried calling
>DrawBorders explicitly with likewise no effect.
>
>Does anyone have any idea what I'm missing here?
>
>Any suggestions would be much appreciated.
>
>Best,
>Bruce

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Bruce L

unread,
May 6, 2008, 3:05:21 PM5/6/08
to
> email: newco...@flounder.com

Thanks for the reply Joe - and the pointer about my weird call to
OnPaint!

I posted this on CodeProject and got an alternative reply:

In order to use CMemDC.h in your controls you need to change the call
to 'FillSolidRectangle(...)' in CMemDC.h:

// Fill background
FillSolidRect(m_rect, pDC->GetBkColor());

to this:

HBRUSH hbrBackGrnd = (HBRUSH)GetClassLong(pDC->GetWindow()-
>GetSafeHwnd(), GCL_HBRBACKGROUND);
::FillRect(GetSafeHdc(), &m_rect, hbrBackGrnd);

Hey Presto! the control borders now get repainted.

0 new messages