⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap5_5.htm

📁 很不错的一本关于VC的书
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<html><head><title>5.5 标签式对话框</title><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><meta name="GENERATOR" content="Microsoft FrontPage 3.0"></head><body link="#3973DE" alink="#3973DE" background="../../bg.gif"><font SIZE="5"><b><div align="center"><center><table border="0" width="85%" cellspacing="0" cellpadding="0" bgcolor="#FFFFFF">  <tr>    </b><td><div align="center"><center><table border="0" width="615" cellpadding="0"    cellspacing="0" height="20">     </table>    </center></div><font FACE="Times New Roman" SIZE="3"><b><p ALIGN="CENTER"></b></font><font    color="#3973DE" FACE="Times New Roman" size="4">5.5 </font><font color="#3973DE" size="4">标签式对话框</font><font    FACE="Times New Roman" size="4"></p>    <p ALIGN="JUSTIFY"></font><span style="font-size: 9pt">在设计较为复杂的对话框时,常常会遇到这种情况:对某一事物的设置或选项需要用到大量的控件,以至于一个对话框放不下,而这些控件描述的是类似的属性,不能分开。用普通的对话框技术,这一问题很难解决。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">MFC提供了对标签式对话框的支持,可以很好的解决上述问题。标签式对话框实际上是一个包含了多个子对话框的对话框,这些子对话框通常被称为页(Page)。每次只有一个页是可见的,在对话框的顶端有一行标签,用户通过单击这些标签可切换到不同的页。显然,标签式对话框可以容纳大量的控件。在象Word和Developer     Studio这样复杂的软件中,用户会接触到较多的标签式对话框,一个典型的标签式对话框如图5.10所示。</span></p>    <p ALIGN="center"><span style="font-size: 9pt"><img src="T5_10.gif"    alt="T5_10.tif (119141 bytes)" WIDTH="447" HEIGHT="247"></span></p>    <p ALIGN="center"><span style="font-size: 9pt">图5.10 典型的标签式对话框</span></p>    <b><p ALIGN="JUSTIFY"></b><span style="font-size: 9pt"><font color="#3973DE">5.5.1     标签式对话框的创建</font></span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">为了支持标签式对话框,MFC提供了CPropertySheet类和CPropertyPage类。前者代表对话框的框架,后者代表对话框中的某一页。CPropertyPage是CDialog类的派生类,而CPropertySheet是CWnd类的派生类。虽然CPropertySheet不是CDialog类的派生类,但使用CPropertySheet对象的方法与使用CDialog对象是类似的。标签式对话框是一种特殊的对话框,因此,和普通对话框相比,它的设计与实现既有许多相似之处,又有一些不同的特点。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">创建一个标签式对话框一般包括以下几个步骤:</span></p>    <blockquote>      <blockquote>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">分别为各个页创建对话框模板,去掉缺省的OK和Cancel按钮。每页的模板最好具有相同的尺寸,如果尺寸不统一,则框架将根据最大的页来确定标签对话框的大小。在创建模板时,需要在模板属性对话框中指定下列属性:</span></p>        <blockquote>          <blockquote>            <p ALIGN="JUSTIFY"><span style="font-size: 9pt">指定标题(Caption)的内容。标题的内容将显示在该页对应的标签中。</span></p>            <p ALIGN="JUSTIFY"><span style="font-size: 9pt">选择TitleBar、Child、ThinBorder和Disable属性。</span></p>          </blockquote>        </blockquote>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">根据各个页的模板,用ClassWizard分别为每个页创建CPropertyPage类的派生类。这一过程与创建普通对话框类的过程类似,不同的是在创建新类对话框中应在Base         class一栏中选择CPropertyPage而不是CDialog。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">用ClassWizard为每页加入与控件对应的成员变量,这个过程与为普通对话框类加入成员变量类似。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">程序员可直接使用CPropertySheet类,也可以从该类派生一个新类。除非要创建一个非模态对话框,或要在框架对话框中加入控件,否则没有必要派生一个新类。如果直接使用CPropertySheet类,则一个典型的标签式对话框的创建代码如清单5.12所示,该段代码也演示了标签式对话框与外界的数据交换。这些代码通常是放在显示对话框的命令处理函数中。可以看出,对话框框架的创建过程及对话框与外界的数据交换机制与普通对话框是一样的,不同之处是还需将页对象加入到CPropertySheet对象中。如果要创建的是模态对话框,应调用CPropertySheet::DoModal,如果想创建非模态对话框,则应该调用CPropertySheet::Create。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">若从CPropertySheet类派生了一个新类,则应该将所有的页对象以成员变量的形式嵌入到派生类中,并在派生类的构造函数中调用CPropertySheet::AddPage函数来把各个页添加到对话框中。这样,在创建标签式对话框时就不用做添加页的工作了。</span></p>      </blockquote>    </blockquote>    <p><b><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">清单5.12     典型的标签式对话框创建代码</span></b></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">void CMyView::DoModalPropertySheet()</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">{</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">CPropertySheet propsheet;</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">CMyFirstPage pageFirst; // derived from     CPropertyPage</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">CMySecondPage pageSecond; // derived from     CPropertyPage</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">// Move member data from the view (or from     the currently</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">// selected object in the view, for     example).</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">pageFirst.m_nMember1 = m_nMember1; </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">pageFirst.m_nMember2 = m_nMember2;</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">pageSecond.m_strMember3 = m_strMember3;</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">pageSecond.m_strMember4 = m_strMember4;</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">propsheet.AddPage(&amp;pageFirst);</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">propsheet.AddPage(&amp;pageSecond);</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">if (propsheet.DoModal() == IDOK)</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">{</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">m_nMember1 = pageFirst.m_nMember1;</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">m_nMember2 = pageFirst.m_nMember2;</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">m_strMember3 = pageSecond.m_strMember3;</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">m_strMember4 = pageSecond.m_strMember4; </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt"><b>. . . </b></span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">}</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">}</span></p>    <b><p ALIGN="JUSTIFY"></b><span style="font-size: 9pt"><font color="#3973DE">.5.2     标签式对话框的运行机制</font></span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">标签式对话框的初始化包括框架对话框的初始化和页的初始化。页的初始化工作可在OnInitDialog函数中进行,而框架对话框的初始化应该在OnCreate函数中完成。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">根据CPropertySheet::DoModal返回的是IDOK还是IDCANCEL,程序可判断出关闭对话框时按的是OK还是Cancel按钮,这与普通对话框是一样的。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">如果标签式对话框是模态对话框,在其底部会有三个按钮,依次为OK、Cancel和Apply(应用)按钮,如果对话框是非模态的,则没有这些按钮。OK和Cancel按钮的意义与普通对话框没什么两样,Apply按钮则是标签对话框所特有的。普通的模态对话框只有在用户按下了OK按钮返回后,对话框的设置才能生效,而设计Apply按钮的意图是让用户能在不关闭对话框的情况下使对话框中的设置生效。由此可见,Apply的作用与前面例子中登录数据的“添加”按钮类似,用户不必退出对话框,就可以反复进行设置,这在某些应用场合下是很有用的。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">为了对上述三个按钮作出响应,CPropertyPage类提供了OnOK、OnCancel和OnApply函数,用户可覆盖这三个函数以完成所需的工作。需要指出的是这三个函数并不是直接响应按钮的BN_CLICKED消息的,但在按钮按下后它们会被间接调用。这些函数的说明如下:</span></p>    <blockquote>      <blockquote>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">virtual void OnOK( );<br>        在按下OK或Apply按钮后,该函数将被调用。缺省的OnOK函数几乎什么也不干,象数据交换和关闭对话框这样的工作是在别的地方完成的,这与普通对话框的OnOK函数是不同的。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">virtual void OnCancel( );<br>        在按下Cancel按钮后,该函数将被调用。缺省的OnCancel函数也是几乎什么都不干。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">virtual BOOL OnApply( );<br>        在按下OK或Apply按钮后,该函数将被调用。缺省的OnApply会调用OnOK函数。函数的返回值如果是TRUE,则对话框中的设置将生效,否则无效。</span></p>      </blockquote>    </blockquote>    <p><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">按理说,CPropertySheet类也应该提供上述函数,特别是OnApply。但奇怪的是,MFC并未考虑CPropertySheet类的按钮响应问题。读者不要指望能通过ClassWizard来自动创建按钮的BN_CLICKED消息处理函数,如果需要用到这类函数,那么只好手工创建了。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">下列几个CPropertyPage类的成员函数也与标签对话框的运行机制相关。</span></p>    <blockquote>      <blockquote>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">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这样的控件通知消息来感知页的内容的改变。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">virtual BOOL OnSetActive( );<br>        当页被激活或被创建时,都会调用该函数。该函数的缺省行为是若页还未创建,就创建之,若页已经创建,则将其激活,并调用UpdateData(FALSE)更新控件。用户可覆盖该函数完成一些刷新方面的工作。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">virtual BOOL OnKillActive( );<br>        当原来可见的页被覆盖或被删除时,都会调用该函数。该函数的缺省行为是调用UpdateData(TRUE)更新数据。用户可覆盖该函数完成一些特殊数据的有效性检查工作。</span></p>      </blockquote>    </blockquote>    <p><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">需要说明的是,标签对话框中的所有页不一定都会被创建。实际上,那些从未打开过的页及其控件是不会被创建的。因此,在CPropertyPage类的派生类中,只有在确定了页已存在后,才能调用与对话框及控件相关的函数(如UpdateData)。如果收到控件通知消息,或OnSetActive函数被调用,则说明页已经存在。正是由于上述原因,使得标签式对话框的内部数据交换只能在OnSetActive和OnKillActive函数中进行。</span></p>    <b><p ALIGN="JUSTIFY"></b><span style="font-size: 9pt"><font color="#3973DE">5.5.3     标签式对话框的具体实例</font></span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">通过上面的分析,读者对标签式对话框已经比较了解了。现在,让我们在前面做过的Register程序中加入一个标签式对话框来试验一下其功能。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">在Register程序的登录数据对话框中有“个人情况”和“单位情况”两组控件,显然,我们可以创建一个标签式对话框并把两组控件分别放到两个页中。为了简单起见,我们仅要求输入姓名和单位名,简化后的标签式对话框如图5.11所示。</span></p>    <p ALIGN="center"><span style="font-size: 9pt"><img src="T5_11.gif"    alt="T5_11.tif (172776 bytes)" WIDTH="556" HEIGHT="189"></span></p>    <p ALIGN="center"><span style="font-size: 9pt">图5.11 简化后的标签式对话框</span><b></p>    <p ALIGN="JUSTIFY"></b><span style="font-size: 9pt"> </span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">通过对标签式对话框的分析,读者已经知道CPropertySheet类未对Apply按钮的控件通知消息进行处理,这是一个不足之处。Register的新版本将向读者演示如何在CPropertySheet类的派生类中手工加入Apply按钮的BN_CLICKED消息处理函数。另外,新版本还演示了对话框与外部对象交流的一种较好办法,即通过发送用户定义消息来向外部对象传递信息。在登录数据对话框中,与外界交流的方法是在对话框内部直接访问派生的视图对象,这样做的优点是方便快捷,缺点则是对外界依赖较大,不利于移植。而用发送用户定义消息的方法则可以避免这个缺点。</span></p>    <p ALIGN="JUSTIFY"><span style="font-size: 9pt">具体工作请按下面几步进行:</span></p>    <blockquote>      <blockquote>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">在菜单资源中的Edit菜单的“登录数据...”项的后面插入一个名为“标签式对话框...”的菜单项,并指定其ID为ID_EDIT_PROPDLG。然后用ClassWizard,在CRegisterView类内为该菜单命令创建命令处理函数OnEditPropdlg,该函数将用来显示标签式对话框。</span></p>        <p ALIGN="JUSTIFY"><span style="font-size: 9pt">为标签式对话框的第一页创建对话框模板。去掉缺省的OK和Cancel按钮。注意应选择中文语种和宋体字体。在属性对话框中,指定对话框的ID为IDD_PERSONAL,标题为“个人情况”,在Styles页中,选中TitleBar项,并在Style栏中选择Child,在Border栏中选择ThinBorder。在More         Styles页中,选中Disable。然后,在模板中加入控件,如图5.11和表5.6所示。</span></p>      </blockquote>    </blockquote>    <p><b><span style="font-size: 9pt"> </span></p>    <p ALIGN="CENTER"><span style="font-size: 9pt">表5.6</span></b></p>    <table BORDER="1" CELLSPACING="1" CELLPADDING="1" WIDTH="579">      <tr>        <td WIDTH="33%"><b><p ALIGN="JUSTIFY"><span style="font-size: 9pt">控件类型</span></b></td>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -