A Word About Property Sheet Notifications

A property sheet sends a notification to the dialog procedure for a page when the page gains or loses the focus or when the user chooses the OK, Cancel, Apply, or Help button. The notifications are sent as WM_NOTIFY messages. The lParam member is a pointer to an NMHDR structure describing the notification. The hwndFrom member contains the window handle of the property sheet, and the hwndTo member contains the window handle of the page.

Some notifications require the dialog procedure to return either TRUE or FALSE in response to the WM_NOTIFY message. For example, if your procedure cannot handle the Apply button, the code that handles the PSN_APPLY notification should respond with a value of TRUE. The return value from the dialog procedure must be set by using the SetWindowLong function rather than by returning TRUE or FALSE. This return value is set in the DWL_MSGRESULT window attribute as follows:

SetWindowLong (hDlg, DWL_MSGRESULT, value);

This is a very important point. I've talked to a great many people who have had problems with their property sheet code, only to find that they are not setting the return value correctly.

Can I Use One Piece of Code for Both a Property Sheet Page and a Dialog Box?

Let's say that you already have a dialog box and a dialog procedure and that you have some odd attachment to the procedure that prevents you from throwing away the code. In fact, you like this code so much that you're wondering whether you can use it for a property sheet page in some cases and for a dialog box in other cases. You can indeed write a single piece of code that works for both a property sheet page and a dialog box, but this is not as easy as having dedicated code for each purpose. If you are using shared code, follow these guidelines:

Hey, My Screen Is Flashing!

You don't have to use a different template for each page of your property sheet. If you like, you can instead use a single template for all the pages and enable/disable or show/hide the controls that are specific to each page on the fly. If you do this, however, the user could encounter annoying screen flashes when switching pages. Your application can minimize or eliminate these flashes by responding to the WM_SHOWWINDOW message. This code snippet demonstrates one method of eliminating the screen flash:

case WM_SHOWWINDOW:
// Check to see whether the window is shown via ShowWindow.
if (wParam && ! LOWORD (lParam))
// It is, so post a message to yourself.
PostMessage (hDlg, WM_APP, 0, 0L);
break;

case WM_APP:
// Remove the rectangle for the page from the invalid list.
ValidateRect (hDlg, NULL);
// Invalidate any and all controls within the page.
InvalidateRect (GetDlgItem (hDlg, ID_CONTROL1), NULL, FALSE);
InvalidateRect (GetDlgItem (hDlg, ID_CONTROL2), NULL, FALSE);
§
InvalidateRect (GetDlgItem (hDlg, ID_CONTROLn), NULL, FALSE);
break;

An application that uses this method repaints only the controls that need repainting inside the page, instead of repainting the whole window when the WM_SHOWWINDOW message is sent. A page will also need to call InvalidateRect with the bErase parameter set to TRUE for controls that do not completely paint their client area during a WM_PAINT message (for example, for a list box that is not full).