Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

Device Contexts in MFC

Device contexts are Win32 objects; they are represented by HDC device-context handles. The MFC provides wrapper classes for the device-context object as the CDC base class, as well as a number of more specialized derived classes.

The basic CDC class is huge and supports all the GDI drawing functions, coordinate mapping functions, clipping functions, font-manipulation and rendering functions, printer-specific functions, path-tracking functions, and metafile-playing functions.

This section briefly covers the various device-context classes and when and how to use and obtain objects of these classes.

The CDC Class

The CDC base class encapsulates all the device-context functionality and drawing functions that use a Win32 HDC object. The actual Win32 device-context handle is accessible via the public m_hDC member. You can retrieve this handle with the device context's GetSafeHdc() function.

You often will be handed a pointer to an initialized CDC object from MFC framework functions, such as CView::OnDraw() and CView::OnBeginPrinting(). These objects are nicely clipped to the dimensions of the window client area so that the results of drawing functions do not appear outside the area of the window.

You also can obtain a pointer to a CDC object for the client area of a window using the CWnd::GetDC() function. If you want a CDC pointer for the entire window area (including the title bar and borders), you can use the CWnd::GetWindowDC() function instead. You can even get a pointer to the entire Windows desktop by calling GetDesktopWindow()->GetWindowDC().

A single Win32 device-context object may be shared among windows of the same registered window class (windows registered with RegisterClass(), not to be confused with Object-Oriented classes). The object may be part of a parent window's device context, or it may be private to the specific window.

After you obtain a pointer to the device context, you can perform a number of drawing operations or other device context–specific functions.

If you have obtained the pointer to a window's device context via GetDC(), however, you must call CWnd::ReleaseDC() on the same CWnd object to release that window's device context for other applications to use.

You also can use the GetDCEx() function, which retrieves the device context and lets you pass a flag value to control the clipping of the returned device context.

The CDC constructor function constructs an uninitialized device context, where the m_hDC member is invalid. You then can initialize it in a number of ways. The CreateDC() function lets you initialize the device context for a specific device with a driver and device name. For example, you can create a device context for the screen like this:

CDC dc;
dc.CreateDC("Display", NULL, NULL, NULL);

Or you could create a device context for a printer like this:

CDC dc;
dc.CreateDC(NULL,"Cannon Bubble-Jet BJ330",NULL,NULL);

The last parameter lets you pass a DEVMODE structure that can be used to set specific initialization defaults. Otherwise, the driver's own defaults are used.

If you just want to retrieve information regarding the capabilities of a device, you can use the CreateIC() function and then use the GetDeviceCaps() function to retrieve the cap abilities of the specified device. The GetDeviceCaps() function can be used with any device context, but a device context created with CreateIC() is a special cut-down device context that only supplies information about a device and cannot be used for drawing operations.

You can use the CreateCompatibleDC() function to create a memory device context compatible with a reference device context supplied as a pointer to a real CDC object. The memory device context is a memory-based representation of the actual device. If you pass NULL as the reference device context, a memory device context is created that is compatible with the screen.

The most common use of a memory device context is to create an offscreen buffer to perform screen capture copy operations via bit-blitting (fast memory copying), or to copy images from the memory device context onto the screen device context.

If you have created a device context (instead of obtaining one via GetDC() or using a pointer to a framework device context), you should delete the Win32 device-context object after use by calling the DeleteDC() member function.

You also can initialize an uninitialized device context from an HDC device-context handle by using the Attach() function and passing the HDC handle. You can detach the handle by calling the corresponding Detach() function, which returns the old HDC handle.

If you just want to quickly create and attach a temporary CDC object to an HDC handle, the FromHandle() function allocates and attaches a CDC object for you, returning the pointer. You should not keep any references to this object, however, because the object is destroyed automatically the next time your program returns to the application's message loop by a call to DeleteTempMap().

You can find the window associated with a device context by calling the device context's GetWindow() function, which returns a CWnd pointer to the associated window.

The device context maintains information relating to coordinate mapping modes, clipping rules, and the current GDI objects used for drawing (such as brushes, pens, bitmaps, and palettes). The details of these are discussed in subsequent sections. You can save and restore all these settings by using SaveDC() and RestoreDC(). When you call SaveDC(), an integer is returned identifying the saved instance (or zero, if it fails). You later can pass this integer to RestoreDC() to restore those saved defaults.

The CClientDC Class

You can use the CClientDC class to quickly connect a CDC object to a window by passing a pointer to the desired window. This is equivalent to constructing a CDC object and attaching an HDC handle obtained from a window with the Win32 ::GetDC() function.

If the constructor succeeds, the m_hWnd member variable is initialized with the handle of the window donating the HDC handle; otherwise, a CResourceException is raised.

When the CClientDC() object falls out of scope or is deleted, the Win32 device context object (HDC) is released automatically.

For example, you might want to access the client device context of a view window from a view-based menu handler to draw a circle, like this:

void CCDCBaseView::OnDrawEllipse()
{
    CClientDC dcClient(this);
    dcClient.Ellipse(0,0,100,100);
}

You will notice that the C++ this pointer simply is passed to the CClientDC constructor to initialize the device context with the view's client device context. After initialization, the circle can be drawn in the view using the CDC::Ellipse() function. Finally, the destructor of the CClientDC object ensures that the view's HDC is released.

The CPaintDC Class

A CPaintDC object is constructed in response to a WM_PAINT message. If you add a handler for this message, you will see that ClassWizard automatically generates an OnPaint() handler starting with the following line:

CPaintDC dc(this); // device context for painting

The CPaintDC constructor automatically calls the BeginPaint() function for you. The BeginPaint() function is responsible for initializing a PAINTSTRUCT structure. PAINTSTRUCT holds information about the smallest rectangle that needs to be redrawn in response to an invalid portion of a window (usually when one window is moved to reveal a portion of the one behind it).

This process is automated in the constructor so that you can immediately use the device context to redraw the invalid area.

The PAINTSTRUCT information is available through the m_ps member, and the attached window handle can be found from the m_hWnd member.

When the object falls out of scope and its destructor is invoked, the EndPaint() function is called to complete the usual WM_PAINT procedure and mark the invalid region as valid.

The CMetaFileDC Class

A Windows metafile is a file that contains a list of GDI drawing instructions required to render an image in a device context. The metafile can be stored on disk, reloaded on another computer with different display capabilities, and redrawn by repeating the drawing instructions to produce a similar image.

You can always play a metafile using the CDC base class's PlayMetaFile() and passing a handle to a metafile object. The CMetaFileDC class lets you also create and record metafiles, however.

To record a metafile, you should construct a CMetaFileDC object. Then call Create() or CreateEnhanced() to create a simple or enhanced metafile on disk passing a filename and a reference device context for the enhanced metafile.

After creating the metafile, you can perform a number of drawing operations in the device context, just as you can in any other device context. The details of these drawing operations are stored in sequence in the metafile as you call each drawing function.

Finally, you should call CMetaFileDC::Close() or CMetaFileDC::CloseEnhanced() to close the metafile. The close functions return an HMETAFILE or HENHMETAFILE handle, which can be used by the Win32 metafile manipulation functions, such as CopyMetaFile() or DeleteMetaFile().

Share ThisShare This

Informit Network