Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
97 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Device Contexts
 Tracking
 Paint Message
 Closing Your
 Application

 Plotting Pixels
 GDI Text Functions
 Displaying Bitmaps
 One Last Thing

 Demo program
 Printable version
 Discuss this article
 in the forums



The Series
 Beginning Windows
 Programming

 Using Resources
 in Win32 Programs

 Tracking Your
 Window/Using GDI

 Introduction
 to DirectX

 Palettes and Pixels
 in DirectDraw

 Bitmapped Graphics
 in DirectDraw

 Developing the
 Game Structure

 Basic Tile Engines
 Adding Characters
 Tips and Tricks

The WM_PAINT Message

A window receives this important message when part of its client area has become invalidated. Suppose your program doesn't have the focus, and the active window is on top of your window. If the user moves that active window, it's going to reveal a part of your window. Since that part of the window needs to be refreshed, it is said to be invalidated. To handle this, there are a couple of things you can do. The first involves a pair of functions designed exclusively for use with the WM_PAINT message. The first is BeginPaint(). Here's the prototype:

HDC BeginPaint( HWND hwnd, // handle to window LPPAINTSTRUCT lpPaint // paint info struct);

Before I tell you exactly what the return value is, let's look at the parameters:

HWND hwnd: This is a handle to the window which needs repainting. You should be used to seeing this parameter by now, right?

LPPAINTSTRUCT lpPaint: Here's the important one. This is a pointer to a PAINTSTRUCT structure, which contains all sorts of information about the area to be painted.

And before we go on, I should show you exactly what a PAINTSTRUCT looks like...

typedef struct tagPAINTSTRUCT { // ps HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT;

And the members of the structure are as follows:

HDC hdc: Aha! I knew there was some reason we went over device contexts, even if it took awile to get here. This is a DC that represents the invalidated area -- the area that needs to be painted.

BOOL fErase: This specifies whether or not the application should erase the background. If set to FALSE, the system has already deleted the background. Remember in our window class when we defined a black brush as the background? This will cause the system to automatically erase the invalidated area with that black brush.

RECT rcPaint: This is the most important member, as it tells you the rectangle that needs to be repainted in order to cover the whole invalidated area. I'll show you the RECT structure in just a bit.

BOOL fRestore, BOOL fIncUpdate, BYTE rgbReserved[32]: Good news! These are reserved and are used by Windows, so you and I don't have to worry about them. :)

Now that I've showed this to you, I can tell you just what BeginPaint() is accomplishing. It actually does three things. First, it validates the window again, so that another WM_PAINT message will not be generated unless the window becomes invalidated again. Second, if your window class has a background brush defined, like ours does, it paints the affected area with that brush. Third, it returns a handle to a device context which represents the area needing to be painted. That area, as we saw, is defined by the important RECT structure:

typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT;

You've already figured out that this structure represents a rectangle, but there is one thing that needs to be said about it. RECTs are upper-left inclusive, but lower-right exclusive. What does that mean? Well, let's say you define a RECT like this:

RECT myRect = {0, 0, 5, 5};

This RECT includes the pixel at (0, 0), but it stops short of (5, 5), so that the lower-right corner of the area described by this rectangle is actually at (4, 4). It doesn't seem to make much sense at first, but you'll get used to the idea.

Now, remember what I said about using device contexts? Once you're done using one, you have to release it. In this case, you use the EndPaint() function. Each call to BeginPaint(), which should only be made in response to a WM_PAINT message, must have a matching EndPaint() function to release the DC. Here's the function:

BOOL EndPaint( HWND hWnd, // handle to window CONST PAINTSTRUCT *lpPaint // pointer to structure for paint data );

The function returns TRUE or FALSE indicating its success or failure, respectively, and takes two simple parameters:

HWND hWnd: Just the handle to the window. Again.

CONST PAINTSTRUCT *lpPaint: A pointer to the PAINTSTRUCT containing the information about the area in question. Don't let the CONST confuse you. It's just there to denote and ensure that the function does not alter the contents of the structure.

For the record, the other way you can validate a window is with a call to ValidateRect(). If you want to do everything manually instead of letting BeginPaint() handle it, that's fine. There may be some cases where this is necessary. So here's the prototype:

BOOL ValidateRect( HWND hWnd, // handle of window CONST RECT *lpRect // address of validation rectangle coordinates );

The return value is TRUE or FALSE for success or failure, and the parameters are easy to figure out:

HWND hWnd: Are you getting tired of seeing this yet? :)

CONST RECT *lpRect: This is a pointer to the RECT to validate. Again, you don't need to declare it as a constant; the CONST is just to make sure the function doesn't go changing things on you. If you pass NULL, the entire client area is validated.

Now, to wrap up our discussion of this message, I'll show you the framework for handling a WM_PAINT message. This would be somewhere in your message handler, as usual. I'm assuming here that we have a global variable called hMainWindow that is the handle to our window.

if (msg == WM_PAINT) { PAINTSTRUCT ps; // declare a PAINTSTRUCT for use with this message HDC hdc; // display device context for graphics calls hdc = BeginPaint(hMainWindow, &ps); // validate the window // your painting goes here! EndPaint(hMainWindow, &ps); // release the DC // tell Windows we took care of it return(0); }

The only part of that code that probably doesn't make sense is the place where I have commented, "Your painting goes here!" Well, if you want your window to be refreshed with something other than your window class's default brush, you have to do it yourself, and that involves some graphics work that you haven't seen yet. Never fear, we'll get there in just a minute! While we're on the topic of messages, though, there's something I need to explain.




Next : Closing Your Application