使用DDV添加更多的属性页
为了使这个向导能够有点用处,我们要为其添加一个设置视图背景颜色的页面。这个页面还将有一个checkbox演示如何处理DDV验证失败并阻止向导进行到下一页。下面就是新的页面,ID是IDD_WIZARD_BKCOLOR:

这个类的实现代码在CWizBkColorPage类中,下面是相关的部分代码
class CWizBkColorPage : public CPropertyPageImpl<CWizBkColorPage>, public CWinDataExchange<CWizBkColorPage> { public: // some stuff removed for brevity...
BEGIN_DDX_MAP(CWizBkColorPage) DDX_RADIO(IDC_BLUE, m_nColor) DDX_CHECK(IDC_FAIL_DDV, m_bFailDDV) END_DDX_MAP()
// Notification handlers int OnSetActive(); BOOL OnKillActive();
// DDX vars int m_nColor;
protected: int m_bFailDDV; }; |
OnSetActive()的工作和前面的介绍页面相同,它使“上一步”和“下一步”按钮可用。OnKillActive()是个新的处理函数,它触发DDV,然后检查m_bFailDDV的值,如果是TRUE就表示checkbox处于选中状态,OnKillActive()将阻止向导进行到下一页。
int CWizBkColorPage::OnSetActive() { SetWizardButtons ( PSWIZB_BACK | PSWIZB_NEXT ); return 0; }
int CWizBkColorPage::OnKillActive() { if ( !DoDataExchange(true) ) return TRUE; // prevent deactivation
if ( m_bFailDDV ) { MessageBox (_T("Error box checked, wizard will stay on this page."),_T("PSheets"), MB_ICONERROR ); return TRUE; // prevent deactivation }
return FALSE; // allow deactivation } |
需要注意的是OnKillActive()中做的事情也可以在OnWizardNext()中完成,因为这两个处理函数都可以使向导维持在当前页面。它们的不同之处在于OnKillActive()在用户单击“上一步”和“下一步”按钮时被调用,而OnWizardNext()只是在用户单击“下一步”按钮时被调用。OnWizardNext()还被用来完成其它目的,比如,它可以直接将向导引导到指定的页面而不是按顺序的下一页。
例子工程的向导还有另外两个页面,CWizBkPicturePage 和 CWizFinishPage,由于它们和前面的两个页面相似,我就不再详细介绍它们,想了解它们的细节可以查看源代码。
其他的界面考虑
置中一个属性表
属性页和向导的默认位置是出现在父窗口的左上角:
这看起来有点不爽,还好有方法可以补救。第一种方法是重载CPropertySheetImpl::PropSheetCallback()函数,在这个函数中将属性表置中。PropSheetCallback()是MSDN中介绍的PropSheetProc()的回调函数,操作系统在属性表创建时调用这个函数,WTL也是利用这个时间子类化属性表窗口的。所以我们的第一种尝试是:
class CAppPropertySheet : public CPropertySheetImpl<CAppPropertySheet> { //... static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam) { int nRet = CPropertySheetImpl<CAppPropertySheet>::PropSheetCallback (hWnd, uMsg, lParam );
if ( PSCB_INITIALIZED == uMsg ) { // center sheet... somehow }
return nRet; } }; |
正如你看到的,我们遇到了棘手的问题。PropSheetCallback()是一个静态方法,不能使用this指针访问属性表窗口。那将这些代码从CPropertySheetImpl::PropSheetCallback()中拷贝出来,然后添加我们自己的方法行不行呢?撇开刚才将代码和特定版本的WTL联系在一起的方法(这已经被证明不是各好方法),现在代码应该是这样的:
class CAppPropertySheet : public CPropertySheetImpl<CAppPropertySheet> { //... static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM) { if(uMsg == PSCB_INITIALIZED) { // Code copied from WTL and tweaked to use CAppPropertySheet // instead of T: ATLASSERT(hWnd != NULL); CAppPropertySheet* pT = (CAppPropertySheet*) _Module.ExtractCreateWndData(); // subclass the sheet window pT->SubclassWindow(hWnd); // remove page handles array pT->_CleanUpPages();
// Our own code follows: pT->CenterWindow ( pT->m_psh.hwndParent ); }
return 0; } }; |
这从理论上讲很完美,但是我试过,属性表的位置并未改变。显然,通用控件的代码在我们调用CenterWindow()之后又改变了属性表窗口的位置。
必须放弃这个将代码封装到属性表类的方法,尽管它是个好的解决方案。我又回到原来的方案,即使用属性页窗口和属性表窗口相互协作是属性表窗口置中。我添加了一个用户定义消息UWM_CENTER_SHEET:
#define UWM_CENTER_SHEET WM_APP CAppPropertySheet 在它的消息映射链中处理这个消息:
class CAppPropertySheet : public CPropertySheetImpl<CAppPropertySheet> { //... BEGIN_MSG_MAP(CAppPropertySheet) MESSAGE_HANDLER_EX(UWM_CENTER_SHEET, OnPageInit) CHAIN_MSG_MAP(CPropertySheetImpl<CAppPropertySheet>) END_MSG_MAP()
// Message handlers LRESULT OnPageInit ( UINT, WPARAM, LPARAM );
protected: bool m_bCentered; // set to false in the ctor };
LRESULT CAppPropertySheet::OnPageInit ( UINT, WPARAM, LPARAM ) { if ( !m_bCentered ) { m_bCentered = true; CenterWindow ( m_psh.hwndParent ); }
return 0; } |
然后,每个属性页的OnInitDialog() 方法发送这个消息到属性表窗口:
BOOL CBackgroundOptsPage::OnInitDialog ( HWND hwndFocus, LPARAM lParam ) { GetPropertySheet().SendMessage ( UWM_CENTER_SHEET );
DoDataExchange(false); return TRUE; } |
添加m_bCentered标志确保属性表窗口只响应收到的第一个UWM_CENTER_SHEET消息。
在属性页中添加图标
如果要使用属性表和属性页的未被成员函数封装的特性,就需要直接访问相关的数据结构:CPropertySheetImpl类中的PROPSHEETHEADER类型(结构)成员m_psh和CPropertyPageImpl类中的PROPSHEETPAGE类型(结构)成员m_psp。
例如:为例子中Option属性表中的Background页面添加一个图标,就需要添加一个标志并设置属性页的PROPSHEETPAGE结构中的几个成员:
CBackgroundOptsPage::CBackgroundOptsPage() { m_psp.dwFlags |= PSP_USEICONID; m_psp.pszIcon = MAKEINTRESOURCE(IDI_TABICON); m_psp.hInstance = _Module.GetResourceInstance(); } |
下面是这些代码的效果:
|