
Abstract
A simple implementation of the memory device context for the WTL.
Objective
The ATL and the WTL are missing some advanced of the advanced drawing
tools that are present in other libraries and frameworks. This article,
and the included source code, provides one such advanced tool: a memory
device context.
Discusssion
Developers quickly learn that drawing directly on the client area of a
window causes flicker. Most professional applications use a technique
known as "double buffered drawing". All drawing operations are
performed on a plane that is invisible to the user. When the scene has
been fully rendered on the hidden plane, its contents are copied to the
plane seen by the user in a single pass.
The WTL does not currently support double buffered drawing. However, it
is very simple to implement. The header file, "AtlGdi.h" contains a
class that implements a Windows Device Context. There are several
subclasses of the CDC object for use in a variety of
situations. However, there is no "Memory Device Context" included in
the WTL. This document describes how to perform double-buffering with
the Win32 SDK, and demonstrates a WTL implementation, based on the CDC
class.
Double-buffering with the SDK
While it is not necessary to understand how double-buffering is
performed in the SDK, it is of interest to see how it works in relation
to the CMemDC described by this article.
While the initialization code for the memory DC can be located in the
WM_CREATE message handler of the window it will be used
with, this does not work when an existing window is subclassed. This
is because the window has to be created before it is subclassed, so the
WM_CREATE message is never seen by the subclass.
Therefore, it is usually desirable to add that code to the
WM_PAINT message handler, with a check to see if the
initialization has already occurred. An example of creating and using a
memory DC:
int WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static HDC hMemDC = 0;
static HBITMAP hBitmap = 0;
int width, height;
HDC hDC;
PAINTSTRUCT ps;
switch( msg )
{
case WM_PAINT:
{
if( ! hMemDC )
{
width = GetSystemMetrics( SM_CXSCREEN );
height = GetSystemMetrics( SM_CYSCREEN );
hDC = GetDC( hWnd );
hMemDC = CreateCompatibleDC( hDC );
hBitmap = CreateCompatibleBitmap( hDC, width, height );
SelectBitmap( hMemDC, hBitmap );
ReleaseDC( hWnd, hDC );
}
...
BeginPaint( hWnd, &ps );
BitBlt( ps.hdc,
ps.rcPaint.left,
ps.rcPaint.top,
ps.rcPaint.right - ps.rcPaint.left,
ps.rcPaint.bottom - ps.rcPaint.top,
hMemDC,
ps.rcPaint.left,
ps.rcPaint.top,
SRCCOPY );
EndPaint( hWnd, &ps );
}
case WM_DESTROY:
{
DeleteDC( hMemDC );
PostQuitMessage( 0 );
}
}
}
Double-buffering with the WTL
The class presented with this article uses virtually the same code, but
is based upon the WTL CDC class. The WM_PAINT
handler in the application creates an instance of CMemDC
with the window handle as an argument:
void OnPaint( HDC )
{
if( ! m_memDC )
{
m_memDC = new CMemDC( *this );
}
...
}
The CMemDC constructor creates the memory DC and the bitmap
internally:
CMemDC( HWND hWnd )
: CDC( )
, m_bitmap( 0 )
, m_hWnd( 0 )
{
ATLASSERT( hWnd );
m_hWnd = hWnd;
int width = ::GetSystemMetrics( SM_CXSCREEN );
int height = ::GetSystemMetrics( SM_CYSCREEN );
CClientDC dc( hWnd );
CreateCompatibleDC( dc );
m_bitmap.CreateCompatibleBitmap( dc, width, height );
SelectBitmap( m_bitmap );
}
The application need only reference the memory DC object instead of
creating a CPaintDC within the WM_PAINT
handler:
void OnPaint( HDC )
{
...
RECT cRect;
GetClientRect( &cRect );
m_memDC->FillSolidRect( &cRect, GetSysColor( COLOR_BTNFACE ) );
m_memDC->DrawEdge( &cRect, EDGE_ETCHED, BF_ADJUST | BF_RECT );
m_memDC->DrawText( _T( "I'm custom drawn."), 17, &cRect,
DT_CENTER | DT_SINGLELINE | DT_VCENTER );
...
}
Then, when the invisible memory DC has been completely painted, its
contents can be copied to the visible window simply by calling its
Paint function:
void OnPaint( HDC )
{
...
m_memDC->Paint( );
}
Also supplied is a Repaint function, which can be used to
refresh the visible window when processing other messages.
Conclusion
Although the implementation of the Memory Device Context class is very
simple, it may be one of the most useful classes in a programmer's
toolbox. Without this class, or something similar, there is no way to
draw on the screen without a great deal of flicker.