Windows Painting and Device Contexts
In Windows, the Graphics Device Interface (GDI) is responsible for displaying graphics and formatted text on output devices such as the screen and printer. To draw on a device, an application uses a device context (DC), which is a Windows data structure containing information about the drawing attributes and capabilities of the output device. Writing text and drawing graphics on a window is known as painting.
System Generated Repaint Requests
Windows does not maintain a permanent copy of an application’s client area. If part of a window is covered, resized, minimised and restored, or otherwise becomes invalid, Windows marks that region as an invalid area and sends the application a WM_PAINT message. Windows keeps track of the size and coordinates of this invalid region for each window.
Before an application can ‘repaint’ the screen, it must obtain a device context for the Windows client area. Windows then fills the device context structure with the attribute values of the device being written to. In MFC, the CDC class wraps a Windows device context and the associated GDI member function for working with the ON_WM_PAINT() handler into one package CPaintDC. The constructor function for CPaintDC is
CPaintDC(CWND *pWnd)
where pWnd is a pointer to the window whose client area will be painted. When creating a device context for the current window, the this pointer is passed to the constructor:
CPaintDC dc(this)
The constructor automatically calls the Windows BeginPaint() function to obtain a device context for the invalid region of the client area. When the CPaintDC object goes out of scope, its destructor automatically calls EndPaint(), releasing the device context and informing Windows that painting is complete.
To respond to a WM_PAINT message in an MFC application, the class must include an ON_WM_PAINT() entry in its message map. This message map entry associates the Windows WM_PAINT message with the class’s OnPaint() member function, which contains the code responsible for repainting the client area.
A typical implementation is:
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyView::OnPaint()
{
CPaintDC dc(this);
// Drawing code goes here
}
When Windows sends a WM_PAINT message, MFC automatically routes the message to the OnPaint() handler, where the application redraws the invalid portion of the window using the CPaintDC object.
Other Useful Device Context-Related Functions
In addition to repainting a window in response to a WM_PAINT message, an application can obtain a device context (DC) whenever it needs to perform drawing operations that are not triggered by the normal painting process.
CClientDC
The CClientDC class creates a device context for the client area of a window that is independent of the OnPaint() handler. It is typically used to perform immediate drawing in response to user actions such as mouse movements or button clicks.
The constructor for CClientDC is:
CClientDC(CWnd* pWnd);
where pWnd is a pointer to the window whose client-area device context is required. To obtain a device context for the current window, pass the this pointer:
CClientDC dc(this);
Passing NULL obtains a device context for the entire screen.
CClientDC (CWND *window)
Where window is a pointer to the window from which the device context is obtained. To invoke a DC for the invoking windows use this as a parameter. To access the entire screen use a NULL pointer.
CWindowDC
The CWindowDC class creates a device context for the entire window, including both the client area and the non-client area.
The non-client area consists of the window border, title bar, menu bar, scroll bars, and the minimise, maximise and close buttons. This area is normally managed by the Windows operating system.
The constructor for CWindowDC is:
CWindowDC(CWnd* pWnd);
where pWnd is a pointer to the window whose device context is required. Passing NULL obtains a device context for the entire screen.
InvalidateRect
The InvalidateRect() function allows an application to manually invalidate part (or all) of a window’s client area. Invalidating a region informs Windows that the area must be repainted. Windows subsequently posts a WM_PAINT message to the application.
void InvalidateRect(LPCRECT lpRect,BOOL bErase = TRUE);
where
lpRect – is a pointer to a RECT structure that contains the update region client coordinates. If this parameter is NULL, the entire client area is set for update.
bErase – Specifies whether the background within the update region is to be erased when the update region is processed. If this parameter is TRUE, the background is erased when the BeginPaint function is called. If this parameter is FALSE, the background remains unchanged.
InvalidateRect() does not repaint the window immediately; it simply marks the specified region as invalid. The repaint occurs later when Windows processes the WM_PAINT message.
ValidateRect
The ValidateRect() function removes a specified region from the update region, informing Windows that the area no longer requires repainting.
void ValidateRect(LPCRECT lpRect);
where:
lpRectis a pointer to aRECTstructure defining the client-area rectangle to be validated.- If
lpRectisNULL, the entire client area is validated, meaning that all pending repaint requests for the window are cancelled.
Unlike InvalidateRect(), which requests that a region be repainted, ValidateRect() clears the update region and prevents Windows from sending a WM_PAINT message for that area.
Example
The following short program demonstrates the OnPaint() message by keeping a running total of the times the client area has been repainted. The repaint request can be generated by dragging another window over the application window or by clicking the minimise and maximise icon