- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Understanding Property Sheets
- Creating a Property Sheet
- Responding to Property Sheet Messages
- Customizing the Standard Property Sheet
- Understanding the Win32 Sequence of Events
- Summary
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Responding to Property Sheet Messages
A host of messages are transmitted around a property sheet during its lifetime. These messages are essentially the glue that holds together the various pages, header control, and parent property sheet window.
The pages themselves act like a series of independent dialog boxes, but a set of activation messages also is sent from the header control to indicate when each page is becoming active or inactive.
Another set of messages is sent to the pages after the OK, Apply, or Cancel button is clicked.
Initializing the Property Pages
After the property sheet has been displayed, the first message that is sent to the initial property page is the WM_INITDIALOG message. You can trap this message in your CPropertyPage-derived class in a normal OnInitDialog() handler function.
Just like a normal dialog box, your property page window and controls are active after the call to the base class CPropertyPage:: OnInitDialog() handler function. At this point, you can call window-based functions on any of your subclassed control-mapping objects (such as those added by ClassWizard), such as EnableWindow(). You also can subclass your own controls inside the OnInitDialog() handler.
As discussed earlier, the WM_INITDIALOG message is sent only once to a property page when it is first displayed. This means that any pages in the property sheet that haven't yet been displayed will not perform OnInitDialog(). Any attempt to access these uninitialized controls—for example, from the property sheet object or a different page object—will cause an assertion.
This only-on-demand initialization of property pages makes for fast and efficient property sheets that need to initialize only the pages that actually are selected by the user.
Property Page Activation and Deactivation
Whenever a particular property page is activated (either for the first time or on subsequent reselection), it is sent a PSN_SETACTIVE notification message from the header control. This notification is handled in the CPropertyPage base class, which then calls the OnSetActive() virtual function. You can provide an override for OnSetActive() in your derived class to perform any initialization required when that page is about to be redisplayed. Your override function should return a TRUE value if the page was initialized successfully, or FALSE if the page was not initialized successfully.
The base class implementation of OnSetActive() calls UpdateData(FALSE) so that your overridden DoDataExchange() function is called to transfer data from your mapped member variables into the page's controls.
When the user then selects a different page, the currently active page is sent the PSN_KILLACTIVE notification, which the CPropertyPage base class translates into a call to the OnKillActive() virtual function. You can stop the new page selection at this point by returning a FALSE value from your own OnKillActive() override function.
The base class implementation of OnKillActive() calls UpdateData(TRUE); this calls through the DoDataExchange() function in save and validate mode. By adding DDX_ and DDV_ macros to your derived class's DoDataExchange() function, you can transfer any data from the controls to your mapped member variables and perform validation specific to your property page. You can add any additional validation not performed in the DoDataExchange() function to OnKillActive() itself. You must remember to display a message box to inform users why the validation may have failed and their new page was not made active (or risk very irate users).
Handling Messages from OK, Apply, and Cancel Buttons
After the property sheet's OK button is clicked, an OnOK() virtual function is called for each of those property pages that have been displayed during the lifetime of the property sheet. Your property pages should override OnOK() to perform any OK handling specific to that page (and that page only). After all of the OnOK() functions have been called and returned in each of the property pages, the property sheet will be closed.
If you want to conditionally stop the property sheet from closing after OK is clicked, you should return a FALSE value from your OnKillActive() overridden function. OnKillActive() is called for the currently active property page just before the OnOK() functions are called.
A corresponding virtual function, OnCancel(), is called in the same way after the Cancel button is clicked. However, the OnKillActive() function isn't called before OnCancel() so that the user can always close the property sheet (unless you explicitly disable the Cancel button). The OnCancel() function actually is called by another virtual function—OnReset(). If you override OnReset() and don't call the base class function, OnCancel() isn't performed. OnReset() is called in response to the PSN_RESET notification.
You can prevent the user from closing the property sheet via Cancel by adding an over ride for OnQueryCancel() that is called just before the OnCancel() function. By returning FALSE from OnQueryCancel(), you can stop the cancellation from proceeding, but a TRUE value lets the cancellation continue as normal.
The Apply button works differently from OK and Cancel. Apply means apply those changes without closing the property sheet. For example, users may want to change the color of a background window and see whether they like it without dismissing the property sheet and still being able to cancel the change. When the property sheet is opened, the Apply button is disabled. Whenever a setting in one of the property pages is changed, you should call that page's SetModified() function, passing a TRUE value. This indicates that the page has been modified, and the Apply button will stay enabled while at least one page is set as modified. You can pass FALSE to SetModified() to reset the page's modified flag.
After the Apply button is clicked, a PSN_APPLY notification is sent to the property pages. This is routed by the base class to call the OnApply() virtual function for each of the initialized property pages (even if their modified flag isn't set). You can return a FALSE value from your OnApply() override to stop the changes from proceeding (and inform the user). Otherwise, you should apply the current changes to your application objects. Remember that the user may want to cancel those changes before closing the property sheet, however, so the original settings also should be preserved.
If you can't preserve the original settings, you can call the CancelToClose() member function of CPropertyPage after applying your changes. After calling this function, the OK button caption is changed to Close, and the Cancel button is disabled to indicate that the changes are irrevocable.
If you don't add an override for OnApply() to your derived property page class, the base class implementation calls the OnOK() virtual function and resets the modified flag in each of the property pages.
You can simulate clicking one of these buttons from within your code by calling the CPropertySheet's PressButton() function from your derived property sheet object. The PressButton() function can be passed PSBTN_OK, PSBTN_CANCEL, or PSBTN_APPLYNOW to simulate the OK, Cancel, or Apply button, respectively. This function passes a PSM_PRESSBUTTON message to the property sheet.
You also can close the property sheet by calling the CPropertySheet's EndDialog() function, which actually posts a PSM_PRESSBUTTON message with a PSBTN_CANCEL value to the property sheet to close it.
Sending Messages Between Property Pages
You can send user-defined messages between the active property pages of a property sheet by calling the pages'QuerySiblings() member function. This sends a PSM_QUERYSIBLINGS message to each of the other property pages in turn, until one of the pages returns a non-zero value. Like other user-defined messages, PSM_QUERYSIBLINGS lets you pass two values, a WPARAM and a LPARAM, which can be passed in the two parameters of the QuerySiblings() function.
Suppose that you have a property sheet with two property pages. You want the second page to request the result of a multiplication from the first page.
The following code implemented in a button message handler in the second property page initiates the interpage communication:
void CSecondPropertyPage::OnMakeChange()
{
SetModified();
int a = 123, b = 456;
LRESULT lRes = QuerySiblings(a,b);
CString strAnswer;
strAnswer.Format("Result of %d * %d = %d",a,b,lRes);
AfxMessageBox(strAnswer);
// Save Changes...
}
The call is made to QuerySiblings() on line 5, passing the two operands for the multiplication request. The result, lRes, which is returned from the query to the second page, then is displayed in the message box in line 8.
The following code implemented in the first property page shows the corresponding message map entry and handler function for the query:
BEGIN_MESSAGE_MAP(CFirstPropertyPage, CPropertyPage)
//{ { AFX_MSG_MAP(CFirstPropertyPage)
// NOTE: the ClassWizard will add message map macros here
//} } AFX_MSG_MAP
ON_MESSAGE(PSM_QUERYSIBLINGS,OnQuerySiblings)
END_MESSAGE_MAP()
8: LRESULT CFirstPropertyPage::OnQuerySiblings(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Got the message, Calculating!");
return wParam * lParam;
}
Notice the message map entry for the PSM_QUERYSIBLINGS message. Its corresponding handler function merely displays a message box and returns the result of the multiplication.
Customizing the Standard Property Sheet | Next Section

Account Sign In
View your cart