|
From: Bruce L on 5 May 2008 20:54 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
From: Joseph M. Newcomer on 5 May 2008 23:10 See below... On Mon, 5 May 2008 17:54:46 -0700 (PDT), Bruce L <Bruce.Lamond(a)gmail.com> wrote: >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); **** 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: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Bruce L on 6 May 2008 15:05 On May 5, 8:10 pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote: > See below... > > > > On Mon, 5 May 2008 17:54:46 -0700 (PDT), Bruce L <Bruce.Lam...(a)gmail.com> wrote: > >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); > > **** > 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 onwww.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: newco...(a)flounder.com > Web:http://www.flounder.com > MVP Tips:http://www.flounder.com/mvp_tips.htm 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.
|
Pages: 1 Prev: OpenService Fails, but GetLastError returns ERROR_SUCCESS Next: Auction Software |