|
楼主 |
发表于 2006-1-13 11:16:08
|
显示全部楼层
5.5.2 标签式对话框的运行机制 <BR><BR> 标签式对话框的初始化包括框架对话框的初始化和页的初始化。页的初始化工作可在OnInitDialog函数中进行,而框架对话框的初始化应该在OnCreate函数中完成。 <BR><BR> 根据CPropertySheet:oModal返回的是IDOK还是IDCANCEL,程序可判断出关闭对话框时按的是OK还是Cancel按钮,这与普通对话框是一样的。 <BR><BR> 如果标签式对话框是模态对话框,在其底部会有三个按钮,依次为OK、Cancel和Apply(应用)按钮,如果对话框是非模态的,则没有这些按钮。OK和Cancel按钮的意义与普通对话框没什么两样,Apply按钮则是标签对话框所特有的。普通的模态对话框只有在用户按下了OK按钮返回后,对话框的设置才能生效,而设计Apply按钮的意图是让用户能在不关闭对话框的情况下使对话框中的设置生效。由此可见,Apply的作用与前面例子中登录数据的“添加”按钮类似,用户不必退出对话框,就可以反复进行设置,这在某些应用场合下是很有用的。 <BR><BR> 为了对上述三个按钮作出响应,CPropertyPage类提供了OnOK、OnCancel和OnApply函数,用户可覆盖这三个函数以完成所需的工作。需要指出的是这三个函数并不是直接响应按钮的BN_CLICKED消息的,但在按钮按下后它们会被间接调用。这些函数的说明如下: <BR><BR> virtual void OnOK( );<BR>在按下OK或Apply按钮后,该函数将被调用。缺省的OnOK函数几乎什么也不干,象数据交换和关闭对话框这样的工作是在别的地方完成的,这与普通对话框的OnOK函数是不同的。 <BR><BR> virtual void OnCancel( );<BR>在按下Cancel按钮后,该函数将被调用。缺省的OnCancel函数也是几乎什么都不干。 <BR><BR> virtual BOOL OnApply( );<BR>在按下OK或Apply按钮后,该函数将被调用。缺省的OnApply会调用OnOK函数。函数的返回值如果是TRUE,则对话框中的设置将生效,否则无效。 <BR><BR> 按理说,CPropertySheet类也应该提供上述函数,特别是OnApply。但奇怪的是,MFC并未考虑CPropertySheet类的按钮响应问题。读者不要指望能通过ClassWizard来自动创建按钮的BN_CLICKED消息处理函数,如果需要用到这类函数,那么只好手工创建了。 <BR><BR> 下列几个CPropertyPage类的成员函数也与标签对话框的运行机制相关。 <BR><BR> void SetModified( BOOL bChanged = TRUE );<BR>该函数用来设置修改标志。若参数bChanged为TRUE,则表明对话框中的设置已改动,否则说明设置未改动。该函数的一个主要用途是允许或禁止Apply按钮。在缺省情况下,Apply按钮是禁止的。只要一调用SetModified(TRUE),Apply按钮就被允许,而调用SetModified(FALSE)并不一定能使Apply按钮禁止,只有在所有被标为改动过的页都调用了SetModified(FALSE)后,Apply按钮才会被禁止。另外,该函数对OnApply的调用也有影响,当Apply按钮被按下后,只有那些被标为改动过的页的OnApply函数才会被调用。在调用该函数之前,程序需要判断页中的内容是否已被修改,可以通过处理诸如BN_CLICKED、EN_CHANG这样的控件通知消息来感知页的内容的改变。 <BR><BR> virtual BOOL OnSetActive( );<BR>当页被激活或被创建时,都会调用该函数。该函数的缺省行为是若页还未创建,就创建之,若页已经创建,则将其激活,并调用UpdateData(FALSE)更新控件。用户可覆盖该函数完成一些刷新方面的工作。 <BR><BR> virtual BOOL OnKillActive( );<BR>当原来可见的页被覆盖或被删除时,都会调用该函数。该函数的缺省行为是调用UpdateData(TRUE)更新数据。用户可覆盖该函数完成一些特殊数据的有效性检查工作。 <BR><BR> 需要说明的是,标签对话框中的所有页不一定都会被创建。实际上,那些从未打开过的页及其控件是不会被创建的。因此,在CPropertyPage类的派生类中,只有在确定了页已存在后,才能调用与对话框及控件相关的函数(如UpdateData)。如果收到控件通知消息,或OnSetActive函数被调用,则说明页已经存在。正是由于上述原因,使得标签式对话框的内部数据交换只能在OnSetActive和OnKillActive函数中进行。 <BR><BR> 5.5.3 标签式对话框的具体实例 <BR><BR> 通过上面的分析,读者对标签式对话框已经比较了解了。现在,让我们在前面做过的Register程序中加入一个标签式对话框来试验一下其功能。 <BR><BR> 在Register程序的登录数据对话框中有“个人情况”和“单位情况”两组控件,显然,我们可以创建一个标签式对话框并把两组控件分别放到两个页中。为了简单起见,我们仅要求输入姓名和单位名,简化后的标签式对话框如图5.11所示。 <BR>><BR>通过对标签式对话框的分析,读者已经知道CPropertySheet类未对Apply按钮的控件通知消息进行处理,这是一个不足之处。Register的新版本将向读者演示如何在CPropertySheet类的派生类中手工加入Apply按钮的BN_CLICKED消息处理函数。另外,新版本还演示了对话框与外部对象交流的一种较好办法,即通过发送用户定义消息来向外部对象传递信息。在登录数据对话框中,与外界交流的方法是在对话框内部直接访问派生的视图对象,这样做的优点是方便快捷,缺点则是对外界依赖较大,不利于移植。而用发送用户定义消息的方法则可以避免这个缺点。 <BR><BR> 具体工作请按下面几步进行: <BR><BR> 在菜单资源中的Edit菜单的“登录数据...”项的后面插入一个名为“标签式对话框...”的菜单项,并指定其ID为ID_EDIT_PROPDLG。然后用ClassWizard,在CRegisterView类内为该菜单命令创建命令处理函数OnEditPropdlg,该函数将用来显示标签式对话框。 <BR><BR> 为标签式对话框的第一页创建对话框模板。去掉缺省的OK和Cancel按钮。注意应选择中文语种和宋体字体。在属性对话框中,指定对话框的ID为IDD_PERSONAL,标题为“个人情况”,在Styles页中,选中TitleBar项,并在Style栏中选择Child,在Border栏中选择ThinBorder。在More Styles页中,选中Disable。然后,在模板中加入控件,如图5.11和表5.6所示。 <BR><BR> <BR><BR> 表5.6 <BR><BR> 控件类型 控件ID 控件标题 <BR> 静态正文 缺省 姓名: <BR> 编辑框 IDC_NAME <BR> <BR> 用ClassWizard为模板IDD_PERSONAL创建CPropertyPage类的派生类,类名为CPersonalPage。在该类中为控件IDC_NAME加入对应的成员变量,变量名为m_strName,类型为CString。为控件IDC_NAME加入EN_CHANGE消息处理函数OnChangeName,当编辑框的内容被改变时,控件会向对话框发出EN_CHANGE消息。在OnChangeName中,应该使Apply按钮允许。 <BR><BR> 仿照步2,为标签式对话框的第二页创建对话框模板。指定其ID为IDD_UNIT,标题为“单位情况”。在模板中加入的控件如图5.11和表5.7所示。 <BR><BR> 表5.7 <BR><BR> 控件类型 控件ID 控件标题 <BR> 静态正文 缺省 工作单位: <BR> 编辑框 IDC_UNIT <BR> <BR> 用ClassWizard为模板IDD_UNIT创建CPropertyPage类的派生类,类名为CUnitPage。在该类中为控件IDC_UNIT加入对应的成员变量,变量名为m_strUnit,类型为CString。为控件IDC_UNIT加入EN_CHANGE消息处理函数OnChangeUnit。 <BR><BR> 用ClassWizard创建一个CPropertySheet的派生类,类名为CRegisterSheet。 <BR><BR> 在CRegisterApp类的头文件的开头加入下面一行<BR>#define WM_USER_OUTPUT (WM_USER+200)<BR>WM_USER_OUTPUT不是标准的Windows消息,而是一个用户定义消息。在本例中,当标签式对话框的Apply按钮被按下后,程序会向编辑视图发送该消息,编辑视图对应的消息处理函数应该输出对话框的数据。用户定义消息的编码范围是WM_USER—0x7FFF。 <BR><BR> 请读者按清单5.13、5.14、5.15修改程序,限于篇幅,这里仅列出了需要修改的部分源代码。 <BR><BR> 清单5.13 CPersonalPage类和CUnitPage类的部分代码 <BR>
<> void CPersonalPage::OnChangeName() <BR><BR> { <BR><BR> // TODO: Add your control notification handler code here <BR><BR> <BR><BR> SetModified(TRUE); //使Apply按钮允许 <BR><BR> UpdateData(TRUE); <BR><BR> } <BR><BR> <BR><BR> void CUnitPage::OnChangeUnit() <BR><BR> { <BR><BR> // TODO: Add your control notification handler code here <BR><BR> <BR><BR> SetModified(TRUE); //使Apply按钮允许 <BR><BR> UpdateData(TRUE); <BR><BR> } </P>
<><BR> 当页中的编辑框的内容被改变时,页会收到EN_CHANGE消息,这将导致OnChangeName或OnChangeUnit被调用。对该消息的处理是使Apply按钮允许并调用UpdateData(TRUE)更新数据。 <BR><BR> 清单5.14 CRegisterSheet类的部分代码 <BR></P>
<> //文件RegisterSheet.h <BR><BR> class CRegisterSheet : public CPropertySheet <BR><BR> { <BR><BR> . . . . . . <BR><BR> // Construction <BR><BR> public: <BR><BR> CRegisterSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); <BR><BR> CRegisterSheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); <BR><BR> <BR> public: <BR><BR> CPersonalPage m_PersonalPage; <BR><BR> CUnitPage m_UnitPage; <BR><BR> . . . . . . <BR><BR> protected: <BR><BR> //{{AFX_MSG(CRegisterSheet) <BR><BR> // NOTE - the ClassWizard will add and remove member functions here. <BR><BR> //}}AFX_MSG <BR><BR> afx_msg void OnApplyNow(); <BR><BR> DECLARE_MESSAGE_MAP() <BR><BR> }; <BR> <BR><BR> //文件RegisterSheet.cpp <BR><BR> #include "stdafx.h" <BR><BR> #include "Register.h" <BR><BR> #include "ersonalPage.h" <BR><BR> #include "UnitPage.h" <BR><BR> #include "RegisterSheet.h" <BR><BR> CRegisterSheet::CRegisterSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage) <BR><BR> :CPropertySheet(pszCaption, pParentWnd, iSelectPage) <BR><BR> { <BR><BR> AddPage(&m_PersonalPage); //向标签对话框中添加页 <BR><BR> AddPage(&m_UnitPage); <BR><BR> } <BR><BR> BEGIN_MESSAGE_MAP(CRegisterSheet, CPropertySheet) <BR><BR> //{{AFX_MSG_MAP(CRegisterSheet) <BR><BR> // NOTE - the ClassWizard will add and remove mapping macros here. <BR><BR> //}}AFX_MSG_MAP <BR><BR> <BR> ON_BN_CLICKED(ID_APPLY_NOW, OnApplyNow) <BR><BR> END_MESSAGE_MAP() <BR> <BR><BR> void CRegisterSheet::OnApplyNow() <BR><BR> { <BR><BR> CFrameWnd* pFrameWnd = (CFrameWnd*) AfxGetMainWnd(); <BR><BR> //获取指向视图的指针 <BR><BR> CView* pView = pFrameWnd->GetActiveFrame()->GetActiveView(); <BR><BR> //发送用户定义消息,在视图中输出信息 <BR><BR> pView->SendMessage(WM_USER_OUTPUT, (WPARAM)this); <BR><BR> m_PersonalPage.SetModified(FALSE); <BR><BR> m_UnitPage.SetModified(FALSE); //使Apply按钮禁止 <BR><BR> } </P>
<><BR> 在CRegisterSheet类内嵌入了CPersonalPage和CUnitPage对象,在该类的构造函数中调用CPropertySheet::AddPage将两个页添加到对话框中。 <BR></P><BR> |
|