|
Just what I needed. Cleared up a number of problems, including the redraw of overlapping objects.
You're properly acknowledged in the "About" dialog of my app. Saved me hours.
MT
http://www.teddev.com
|
|
|
|
|
Could anyone offer any advice on using this technique with a dialog-based app (if it's even possible). I'm new to the game and I'm trying to get rid of some update-related flicker in a progress control that I'm stepping through with a timer. Any advice would be appreciated.
blitzn
|
|
|
|
|
if you are subclassing your progress control u can do this
#include "memdc.h"
void CMyProgressCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rect;
GetClientWindow(&rect);
CMemDC memDC(&dc,&rect);
// do your updating of your progress control
memDC.FillSolidRect(.........);
}
|
|
|
|
|
As I said, it does not work
|
|
|
|
|
In case anyone wants it, I have added some additional code into memdc.h that will allow it to support non-MFC C++ and straight C via ifdefs. Let me know here if you are interested.
|
|
|
|
|
I have received enough requests that I have decided to post it here.
<br />
<br />
<br />
#ifndef _MEMDC_H_<br />
#define _MEMDC_H_<br />
#else<br />
#error this file has already been included<br />
#endif<br />
<br />
#ifdef __cplusplus<br />
<br />
<br />
#ifdef _AFXDLL // for use with MFC<br />
<br />
<br />
class CMemDC : public CDC<br />
{<br />
private: <br />
CBitmap m_bitmap;
CBitmap* m_oldBitmap;
CDC* m_pDC;
CRect m_rect;
BOOL m_bMemDC;
public:<br />
<br />
CMemDC(CDC* pDC, const CRect* pRect = NULL) : CDC()<br />
{<br />
ASSERT(pDC != NULL); <br />
<br />
m_pDC = pDC;<br />
m_oldBitmap = NULL;<br />
m_bMemDC = !pDC->IsPrinting();<br />
<br />
if( pRect == NULL )<br />
{<br />
pDC->GetClipBox(&m_rect);<br />
}<br />
else<br />
{<br />
m_rect = *pRect;<br />
}<br />
<br />
if( m_bMemDC )<br />
{<br />
CreateCompatibleDC(pDC);<br />
pDC->LPtoDP(&m_rect);<br />
<br />
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());<br />
m_oldBitmap = SelectObject(&m_bitmap);<br />
<br />
SetMapMode(pDC->GetMapMode());<br />
SetWindowExt(pDC->GetWindowExt());<br />
SetViewportExt(pDC->GetViewportExt());<br />
pDC->DPtoLP(&m_rect);<br />
SetWindowOrg(m_rect.left, m_rect.top);<br />
}<br />
else<br />
{<br />
m_bPrinting = pDC->m_bPrinting;<br />
m_hDC = pDC->m_hDC;<br />
m_hAttribDC = pDC->m_hAttribDC;<br />
}<br />
<br />
COLORREF bgc = pDC->GetBkColor();<br />
FillSolidRect( m_rect, bgc );<br />
}<br />
<br />
<br />
~CMemDC() <br />
{ <br />
if( m_bMemDC )<br />
{<br />
m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),<br />
this, m_rect.left, m_rect.top, SRCCOPY); <br />
<br />
SelectObject(m_oldBitmap);<br />
}<br />
else<br />
{<br />
m_hDC = m_hAttribDC = NULL;<br />
} <br />
}<br />
<br />
CMemDC* operator->() { return this; } <br />
<br />
operator CMemDC*() { return this; }<br />
};<br />
<br />
<br />
#endif // _AFXDLL<br />
<br />
<br />
<br />
class CMemoryDC<br />
{<br />
private:<br />
HBITMAP m_hbitmap;<br />
HBITMAP m_holdbmp;<br />
HDC m_hDC;<br />
HDC m_holdDC;<br />
RECT m_rBounds;<br />
int m_Width;<br />
int m_Height;<br />
<br />
public:<br />
CMemoryDC( HDC hDC, const RECT *rBounds )<br />
{<br />
m_holdDC = hDC;<br />
m_hDC = CreateCompatibleDC( hDC );<br />
memcpy( &m_rBounds, rBounds, sizeof( RECT ) );<br />
m_Width = m_rBounds.right - m_rBounds.left;<br />
m_Height = m_rBounds.bottom - m_rBounds.top;<br />
m_hbitmap = CreateCompatibleBitmap( hDC, m_Width, m_Height );<br />
m_holdbmp = (HBITMAP)SelectObject( m_hDC, m_hbitmap );<br />
}<br />
<br />
~CMemoryDC() <br />
{<br />
BitBlt( m_holdDC, m_rBounds.left, m_rBounds.top, m_Width, m_Height, <br />
m_hDC, m_rBounds.left, m_rBounds.top, SRCCOPY );<br />
SelectObject( m_holdDC, m_holdbmp );<br />
if( m_hbitmap != NULL ) DeleteObject( m_hbitmap );<br />
if( m_hDC != NULL ) DeleteDC( m_hDC );<br />
}<br />
<br />
HDC GetDC() { return m_hDC; }<br />
<br />
HDC operator->() { return m_hDC; }<br />
};<br />
<br />
<br />
#else // __cplusplus<br />
<br />
<br />
#ifndef _INC_MALLOC<br />
#include <malloc.h><br />
#endif<br />
<br />
<br />
typedef struct<br />
{<br />
HBITMAP m_hbitmap;<br />
HBITMAP m_holdbmp;<br />
HDC m_hDC;<br />
HDC m_holdDC;<br />
RECT m_rBounds;<br />
int m_Width;<br />
int m_Height;<br />
} MemDC;<br />
<br />
<br />
__inline MemDC * InitMemDC( HDC hDC, const RECT *rBounds )<br />
{<br />
MemDC * mdc = calloc( 1, sizeof( MemDC ) );<br />
if( ! mdc )<br />
return NULL;<br />
<br />
mdc->m_holdDC = hDC;<br />
mdc->m_holdbmp = NULL;<br />
<br />
mdc->m_hDC = CreateCompatibleDC( hDC );<br />
memcpy( &mdc->m_rBounds, rBounds, sizeof( RECT ) );<br />
mdc->m_Width = mdc->m_rBounds.right - mdc->m_rBounds.left;<br />
mdc->m_Height = mdc->m_rBounds.bottom - mdc->m_rBounds.top;<br />
mdc->m_hbitmap = CreateCompatibleBitmap( hDC, mdc->m_Width, mdc->m_Height );<br />
mdc->m_holdbmp = (HBITMAP)SelectObject( mdc->m_hDC, mdc->m_hbitmap );<br />
return mdc;<br />
}<br />
<br />
<br />
__inline HDC GetMemDC( MemDC *mdc )<br />
{<br />
return mdc->m_hDC;<br />
}<br />
<br />
<br />
__inline MemDC * TermMemDC( MemDC *mdc )<br />
{<br />
BitBlt( mdc->m_holdDC, mdc->m_rBounds.left, mdc->m_rBounds.top,<br />
mdc->m_Width, mdc->m_Height, mdc->m_hDC,<br />
mdc->m_rBounds.left, mdc->m_rBounds.top, SRCCOPY );<br />
SelectObject( mdc->m_holdDC, mdc->m_holdbmp );<br />
if( mdc->m_hbitmap != NULL )<br />
DeleteObject( mdc->m_hbitmap );<br />
if( mdc->m_hDC != NULL )<br />
DeleteDC( mdc->m_hDC );<br />
free( mdc );<br />
return NULL;<br />
}<br />
<br />
<br />
#endif // __cplusplus<br />
<br />
|
|
|
|
|
hi,
you should account for left and top of rBounds in CMemoryDC ctor like this
// offset dc origin
SetWindowOrgEx(m_hDC, m_rBounds.left, m_rBounds.top, NULL);
you don't need m_Width and m_Height if you have m_rBounds -- just calc them on the fly.
you can check for rBounds == NULL and do a GetClipBox in the ctor the way the original class is doing. an assert would be nice if m_rBounds.left == rBounds.right (no clipbox found).
nice piece of code, didn't notice it before and just finished re-implementing it myself )
HTH,
</wqw>
|
|
|
|
|
The MemDC works fine, even in a ScrollView. The only problem I can see by now is using DrawDragRect. If ScrollPos is not (0,0), I have to call LPtoDP on the drag rect before calling DrawDragRect. Why?
|
|
|
|
|
I am having trouble with CListView and the CMemDC class. It works fine for the columns that have been inserted but not for the rest of the screen. It is a simple listview from the wizard with CListView as the base class. I then add three columns and fill up about 10 rows. but only the first ten rows are cleared of the background image. The rest show the window that was on the screen before the app was run.
As more rows are added then they are cleared as well. Also, if the window is opened beyond the 3 columns then the extra space on the right is also not cleared up.
I am fairly new to MFC and I have tried to adjust how the rect is created with no luck. I thought if I created a larger bitmap it would work.
Any help/or ideas would be apprecitated. And I appologize if I am missing something simple here.
Thanks,
John
|
|
|
|
|
---
Eugene Pustovoyt
Sonork ID 100.10002:Yaumen
|
|
|
|
|
There's an error in your usage of OnEraseBackground(). It should return TRUE, not FALSE.
Great article otherwise; I've been using variants of this class for years, and it's (IMHO) essential to creating good-looking apps.
|
|
|
|
|
MemDc work under MM_TEXT mode, doesn't work under other modes?
|
|
|
|
|
I think you need to provide more info. The example included with CMemDC demonstrates using it in most modes including MM_TEXT. The only thing I'm aware of that could be a problem is using it with a CScrollView.
So, please send me a small example the demonstrates the issue you are having and why you believe this is a mapping mode issue with CMemDC.
Thanks,
Keith
|
|
|
|
|
1) What is the purpose of the two functions that allow usage as a pointer? I've been passing pointers to classes for years without adding such function to my classes.. including this class.
2) This class sure has grown since it's original lightweight implementation. Isn't the constructor much too big to be an inline function? (That is, an _efficient_ inline function.)
|
|
|
|
|
To simplify the drawing routine in an application, I decided to use this class. It does a lot better. However, it assumes you are always going to be painting the background and therefore uses FillRectSolid to clear the exising background.
This was not suitable for my work, which involves a lot of already intensive drawing using memory dc with CMemDC class only doing additional drawing on top.
To get it to work for me, I modified the class a bit, and here is the result-please let me know if you find problems with it.
class CMemDC : public CDC
{
private:
CBitmap m_bitmap;
CBitmap* m_oldBitmap;
CDC* m_pDC;
CRect m_rect;
BOOL m_bMemDC;
CDC m_dcBk;
CBitmap* m_pbmpOldBk;
CBitmap m_bmpBk;
public:
CMemDC(CDC* pDC, const CRect* pRect = NULL, BOOL bCopyBk = FALSE) : CDC()
{
ASSERT(pDC != NULL);
m_pbmpOldBk = NULL;
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC = !pDC->IsPrinting();
if (pRect == NULL)
{
pDC->GetClipBox(&m_rect);
}
else
{
m_rect = *pRect;
}
if (m_bMemDC)
{
CreateCompatibleDC(pDC);
pDC->LPtoDP(&m_rect);
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
m_oldBitmap = SelectObject(&m_bitmap);
SetMapMode(pDC->GetMapMode());
pDC->DPtoLP(&m_rect);
SetWindowOrg(m_rect.left, m_rect.top);
}
else
{
m_bPrinting = pDC->m_bPrinting;
m_hDC = pDC->m_hDC;
m_hAttribDC = pDC->m_hAttribDC;
}
if (!bCopyBk)
{
FillSolidRect(m_rect, pDC->GetBkColor());
}
else
{
CWnd* pWnd = m_pDC->GetWindow();
ASSERT(pWnd);
CClientDC clDC(pWnd);
m_dcBk.CreateCompatibleDC(&clDC);
m_bmpBk.CreateCompatibleBitmap(&clDC, m_rect.Width(), m_rect.Height());
m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
m_dcBk.BitBlt(0, 0, m_rect.Width(), m_rect.Height(), &clDC, m_rect.left, m_rect.top, SRCCOPY);
BitBlt(0, 0, m_rect.Width(), m_rect.Height(), &m_dcBk, 0, 0, SRCCOPY);
}
}
~CMemDC()
{
if (m_bMemDC)
{
m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
this, m_rect.left, m_rect.top, SRCCOPY);
if (m_pbmpOldBk)
SelectObject(m_pbmpOldBk);
SelectObject(m_oldBitmap);
}
else
{
m_hDC = m_hAttribDC = NULL;
}
}
CMemDC* operator->()
{
return this;
}
operator CMemDC* ()
{
return this;
}
};
#endif
Paul Selormey, Bsc (Elect Eng), MSc (Mobile Communication) is currently Windows open source developer in Japan, and open for programming contract anywhere!
|
|
|
|
|
The code you provided saved a project of mine from the recycle bin! Thanks!
|
|
|
|
|
This is really a helpful class, and it solved the flicker problem.
Here is one issue that I saw. I used it in my CScrollView (I think other view might have the same issue). If I pops up a message box (or any window) on the top of the view, and move the box around, the CScrollView doesn't refresh the background, so you can see all the movement tracks on your CScrollView.
I'm guessing this is probably caused by return "FALSE" in OnEraseBkgnd() method. I'm wondering why you are doing this? Or can you solve the problem that I described above?
Thanks,
Jerry
|
|
|
|
|
Sir when i view a bitmap on View .I apply some image operations If another window is opened over it the covering portion is erased any clue;)
my email id amritpalbhatia@rediffmail.com
|
|
|
|
|
I use a CScrollView derived class and I don't get the whole view redrawn when I use CMemDC and the Scrollposition is different than the origin...
anybody?
|
|
|
|
|
Danial
I found a similar problem. It seems that GetClipRect doesn't work properly after the SetWindowOrg call. (I was using GetClipRect and then creating a HRGN object from it) I replaced my GetClipRect with a call to GetClipRgn and the whole window was updated properly.
If you have the source for CScrollView handy, you might want to check to see if it also uses GetClipRect.
Pete
____________________________
Peter Donahue
Software Developer
COM DEV Space
Cambridge, ON, Canada
|
|
|
|
|
Perfect! This was just what I needed. easy to use too!
Erik
|
|
|
|
|
hmmm, thanks, pal. it really helped, simply great.
|
|
|
|
|
Your code solved my problem to perfection! I have a spreadheet type view where the user can resize
columns by dragging their right borders. Using SetCapture and redrawing in OnMouseMove let the user
see in real time what the effect of the column width adjustment was, but with very noticeable
flicker. Your code eliminated the flicker entirely.
Strangely, however, OnDraw does not redraw properly in response to scrolling now. I managed to
take care of that by calling a duplicate OnDraw(without the memory dc) whenever the mouse was
NOT captured.
Do you know why scrolling the view window(like a user would normally do)breaks the Ondraw that
uses the memory dc?
Regards and THANKS
Ed
|
|
|
|
|
You wouldn't know of a fast way to initialise a memdc with the contents of an on-screen dc would you ?
Without an init the memdc is filled with garbage & only the bit painted in looks like it is supposed to.
I need to do this because what is drawn in any one OnPaint call differs depending on what is already in the DC.
A simple BitBlt works but is incredibly slow.???
A Non-MFC example would be prefered.
Also - do you know of any methods/add-ins of debugging DCs - like showing their contents etc at runtime - that would integrate well with MSVC++6
Seeya
Pab
|
|
|
|
|
If you create a DC like this:
CWindowDC dc(NULL);
you will get a dc that contains the screen image. I don't know if you knew this, as this is how I would do the BitBlt you refer to ( get the screen contents into another DC ). GDI is a thin wrapper over Win32 functions, for example dc.BitBlt merely calls the Win32 function. There is no faster way to copy a chunk of image data that I know of, I assume BitBlt does a memcpy.
As for debugging, once you have this CWindowDC, you can draw to it, which is how I keep track of the contents of different DC's when debugging GDI code.
Christian
I've learned that you cannot make someone love you. All you can do is stalk them and hope they panic and give in.
The early bird may get the worm, but it's the second mouse that gets the cheese.
|
|
|
|
|