In one of the dialog classes, override the OnApply
button. It
is not necessary to do this in all of the dialog classes, since clicking
the Apply
button calls the OnApply
method for each
of the dialog classes. OnApply
has a default implementation
in the CProperyPage
class. If each page has a different action
to be performed when Apply is clicked, then it is possible to override
OnApply
in more than one class. In this example, when the
OnApply
button is pressed, then a message is sent to the
CView
. Only one message needs to be sent, so only one
OnApply
needs to be overriden.
It is the programmer's responsibility to define when the Property Sheet changes.
Usually, this will happen in response to a user's action. For instance, if
a radio button changes or the value in an edit box changes, then the information
in the Property Sheet has changed. To set the modified flag when such actions
occur, map event handlers for these actions. In the event handler, call the
SetModified
method for the property page.
Here is an example of a property page named CPage1. When the user changes the edit box named Edit1, the modifed flag is set.
void CPage1::OnEnChangeEdit1() { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CPropertyPage::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here SetModified(); }
After following the above steps, the Apply
button will now function,
but the view is still not being changed. It is neessary to send a message
to the view from the OnApply
button. When the view receives
this message, it will update itself.
Define a message in the header file for the page that overrode the
OnApply
method. There is a constant that defines the first number
that can be used by the user for defining constants: WM_USER
.
By using this constant as a base, then it can be guaranteed that the user's
constant definitions will not interfere with other constants already defined
in the application.
//page1.h
#define WM_PROPCHANGE WM_USER + 6
In order to send a message to the view, the property page needs to have a
pointer to the view. In the Property Page class that has overriden the
OnApply
method, add a member variable of type
CView*
CView* m_pView;
and a method that will set it.
void CPage1::SetView(CView* pView) { m_pView = pView; }
In the Property Sheet class constructor, call the SetView
method
of the Property Page, m_page1
, that has overriden the
OnApply
button.
CPropSheet::CPropSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage) :CPropertySheet(pszCaption, pParentWnd, iSelectPage) { AddPage(&m_page1); AddPage(&m_page2); m_page1.SetView((CView*) pParentWnd); }
When using the Apply
button, the Property Sheet needs to be
accessible from two different member functions. Therefore, it will be necessary
to change the CPropertySheet
variable in the
OnFilePropertysheet
method into a member variable. Because the
constructor of the Property Sheet is being sent a pointer from the View,
it will be necessary to create the Property Sheet as a pointer.
Note: The alternative is to create the Property Sheet as a variable,
and to initialize it in the constructor's initialization list. The problem
with this approach is that the pointer to this
cannot be used
in an initialization list without generating a warning.
CPropSheet* m_pPropSheet;
CPropsView::CPropsView() { m_edit = "Default"; m_check = FALSE; m_pPropSheet = new CPropSheet("Property Sheet", this, 0); } CPropsView::~CPropsView() { delete m_pPropSheet; }
void CPropsView::OnFilePropertysheet() { m_pPropSheet->m_page1.m_edit = m_edit; m_pPropSheet->m_page2.m_checkbox = m_check; int result = m_pPropSheet->DoModal(); if (result == IDOK) { m_edit = m_pPropSheet->m_page1.m_edit; m_check = m_pPropSheet->m_page2.m_checkbox; Invalidate(); } }
In the OnApply
method, send the message to the view. There are
two optional arguments to the PostMessage
method. They are not
needed in this example, but two values are being sent just as a demonstation.
BOOL CPage1::OnApply() { // TODO: Add your specialized code here and/or call the base class m_pView->PostMessage(WM_PROPCHANGE, 1, 2); return CPropertyPage::OnApply(); }
The framework wizards cannot map user defined messages.
In the header for the view class, add the following method. All user defined message handlers have this format.
afx_msg LRESULT OnPropChange(WPARAM wParam, LPARAM lParam);
In the cpp class for the view, include the header for the Property
Page that overrode the OnApply
method. Also, add the message
map for the user defined message. User defined messages use the
ON_MESSAGE
macro to map a message to a method.
#include "Page1.h"BEGIN_MESSAGE_MAP(CPropsView, CView)// ... other message maps are here ... // add this one to the list that is already there ON_MESSAGE(WM_PROPCHANGE, OnPropChange) END_MESSAGE_MAP()
In the implementation of the OnPropChange
method, update the
variables that control the view, and invalidate the client window. The
wParam
and lParam
parameters are not needed, but
they are sent to the output window just to demonstrate that additional
information can be sent with the message.
LRESULT CPropsView::OnPropChange(WPARAM wParam, LPARAM lParam) { TRACE("%d %d\n", wParam, lParam); m_edit = m_pPropSheet->m_page1.m_edit; m_check = m_pPropSheet->m_page2.m_checkbox; Invalidate(); return 0L; }
Example: Props