Stingray® Foundation : Chapter 10 GDI Classes : Device Contexts
Device Contexts
The Device Context (DC) is the main abstraction used in GDI programming. The use of a Device Context allows the programmer to write output code that is device-independent. The same calls can be used to send output to the display and the printer, the specific details of each device are taken care of by a device driver with no intervention from the programmer.
The class that encapsulates a DC in the SFL Graphics package is CGraphicsContext. This class provides an inline wrapper method for every API function in the Win32 GDI. It also has methods for attaching and detaching a plain DC handle to a CGraphicsContext.
To use the CGraphicsContext class, you first have to provide a DC handle obtained somehow, as shown in Example 90. After that, you can call all the GDI functions you want.
Example 90 – Using the CGraphicsContext class
void DrawTracker (
HDC hdcTracker,
CRect rcTracker
)
{
CSize szTracker(0, 0);
CGraphicsContext dc(hdcTracker);
 
<...>
 
dc.DrawDragRect(rcTracker, szTracker, rcLastTracker, szTracker);
}
Device Context Creation and Destruction
Noticeably absent from the CGraphicsContext public interface are methods for creation of a device context. There is a ReleaseHandle() method, but its implementation is a no-op.
The reason for this is that there is no single way to create and release device contexts in the Win32 API. The GDI API distinguishes multiple types of device contexts; each one of them has a different creation process, accompanied by the corresponding destruction process.
SFL provides several specializations of CGraphicsContext, each one corresponding to one of the DC types distinguished by the GDI. These are:
CPaintGraphicsContext: Used in the WM_PAINT handler of a window. Allows output on the invalidated area of the window.
CClientGraphicsContext: Allows output on the entire client area of a window.
CWindowGraphicsContext: Permits output on any point of the window area, including client and non-client space.
CDeviceGraphicsContext: Associated with a specific device driver. Usually used for printing.
CMemoryGraphicsContext: Device context not directly associated with any physical output device.
CMetafileGraphicsContext: Output to a metafile, a file that contains GDI instructions that can be reproduced later on an actual output device.
Each one of these classes publishes a Create() method that creates and initializes a new DC of the type corresponding to that class. The set of parameters taken by this method varies from class to class, since different types of DCs require a different set of initialization data.
SFL’s plain CGraphicsContext class does not follow an ownership scheme like the GDI objects. An instance of CGraphicsContext never owns the DC handle it contains; therefore it never destroys it when the object instance gets destroyed.
An instance of any specialized Graphics Context class always owns the DC handles that it contains. This is because it was that instance which originally created the contained handle; and, therefore, it is the only one who has the knowledge of how to deallocate it.
As shown in Example 91, let’s consider a handler for the WM_PAINT message:
Example 91 – A handler for the WM_PAINT message
LRESULT OnPaint(UINT , WPARAM , LPARAM , BOOL& )
{
CPaintGraphicsContext dcPaint(*this);
dcPaint.Rectangle(CRect(0, 0, 100, 100));
return 0;
}
The code above paints a rectangle on the upper left corner of the window. Notice that no explicit destruction call is necessary; the device context is released appropriately when the dcPaint variable goes out of scope.
Below is Example 92, in which a Client DC is created for a window, and a compatible Memory DC is created to display a bitmap on the painting area of that window.
Example 92 – Creating a Client DC and a Memory DC
void DisplayBitmap (
GDIBitmap& bmpToDisplay
)
{
CClientGraphicsContext dcClient(*this);
CMemoryGraphicsContext dcBitmap(dcClient);
 
CGDIBitmap bmpOld = dcBitmap.SelectObject(bmpToDisplay);
 
CSize szToDisplay = bmpToDisplay.GetBitmapSize();
dcClient.BitBlt(CRect(CPoint(0, 0), szToDisplay), dcBitmap,
CPoint(0, 0), SRCCOPY);
 
dcBitmap.SelectObject(bmpOld);
return 0;
}
MFC Compatibility
It is very likely that you already have some complex painting code in some MFC applications, and you would like to be able to migrate that code to your new SFL-based applications with the least amount of work possible.
In order to maintain source code level compatibility with legacy MFC code, SFL’s Graphics package has a compatibility layer that enables you to do precisely that.
If you are not using MFC in conjunction with SFL’s Graphics package (concretely, if the preprocessor flag _SFL_MFC_SUPPORT is not defined), the MFC names for GDI objects (i.e. CPen, CBrush, CDC, etc.) are defined as synonyms of the native SFL names described in the previous sections.
When you are using MFC in conjunction with SFL, the MFC names belong to MFC. If you wish to use the SFL classes, you will have to address them by their native names.
Most of the method names and signatures in MFC have been respected. However, there are some aspects where the behavior differs in both libraries. For example, calls to CDC::FromHandle() will not compile in SFL. SFL does not manage a global map of temporary objects like MFC does, therefore it is not capable of returning an instance that you have created elsewhere in the program. You have to keep track of your own objects in SFL, and manage temporary objects accordingly. Remember, however, that the plain CGraphicsContext class can be attached to any DC handle with no harm, since it does not release the handle when destroyed; this technique can be a substitute for the FromHandle() function.