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

📄 wtl for mfc programmers, part viii - property sheets and wizard.htm

📁 MFC程序员的WTL指南,非常具体,WTL入门最好的书
💻 HTM
📖 第 1 页 / 共 3 页
字号:
 
    <SPAN class=cpp-comment>// Maps</SPAN>
    BEGIN_MSG_MAP(CBackgroundOptsPage)
        MSG_WM_INITDIALOG(OnInitDialog)
        CHAIN_MSG_MAP(CPropertyPageImpl&lt;CBackgroundOptsPage&gt;)
    END_MSG_MAP()
 
    BEGIN_DDX_MAP(CBackgroundOptsPage)
        DDX_RADIO(IDC_BLUE, m_nColor)
        DDX_RADIO(IDC_ALYSON, m_nPicture)
    END_DDX_MAP()
 
    <SPAN class=cpp-comment>// Message handlers</SPAN>
    BOOL OnInitDialog ( HWND hwndFocus, LPARAM lParam );
 
    <SPAN class=cpp-comment>// Property page notification handlers</SPAN>
    <SPAN class=cpp-keyword>int</SPAN> OnApply();
 
    <SPAN class=cpp-comment>// DDX variables</SPAN>
    <SPAN class=cpp-keyword>int</SPAN> m_nColor, m_nPicture;
};</font></PRE>
      
<P>关于这个类需要注意几点:
<UL>
  <LI>有一个名为IDD的公有成员将对话框于资源联系起来。
  <LI>消息映射链和CDialogImpl相似。
  <LI>消息映射链将消息链入CPropertyPageImpl,从而使我们能够处理与属性表相关的通知消息。
  <LI>有一个OnApply()处理函数在单击属性表中的OK按钮时保存用户的选择。</LI>
</UL>
      
<P>OnApply() 非常简单,它调用 DoDataExchange() 更新 DDX 变量,然后返回一个代码标识是否可以关闭这个属性表:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">int</font></SPAN><font color="#0000FF"> CBackgroundOptsPage::OnApply()
{
    <SPAN class=cpp-keyword>return</SPAN> DoDataExchange(<SPAN class=cpp-keyword>true</SPAN>) ? PSNRET_NOERROR : PSNRET_INVALID;
}</font></PRE>
      
<P>我们还要在主窗口添加一个Tools|Options菜单来打开属性表,这个菜单的处理函数创建一个属性表,但是添加了一个新属性页CBackgroundOptsPage。</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">void</font></SPAN><font color="#0000FF"> CMainFrame::OnOptions ( UINT uCode, <SPAN class=cpp-keyword>int</SPAN> nID, HWND hwndCtrl )
{
CPropertySheet sheet ( _T(<SPAN class=cpp-string>"PSheets Options"</SPAN>), <SPAN class=cpp-literal>0</SPAN> );
CBackgroundOptsPage pgBackground;
CPropertyPage&lt;IDD_ABOUTBOX&gt; pgAbout;
 
    pgBackground.m_nColor = m_view.m_nColor;
    pgBackground.m_nPicture = m_view.m_nPicture;
 
    sheet.m_psh.dwFlags |= PSH_NOAPPLYNOW;
 
    sheet.AddPage ( pgBackground );
    sheet.AddPage ( pgAbout );
 
    <SPAN class=cpp-keyword>if</SPAN> ( IDOK == sheet.DoModal() )
        m_view.SetBackgroundOptions ( pgBackground.m_nColor,
                                      pgBackground.m_nPicture );
}</font></PRE>
      
<P>属性表的构造函数的第二个参数是0,表示将索引是0的页面初始是可见的,你可以将其设为1,使得属性表第一次显示时显示关于页面。既然是演示代码,我就偷个懒,使用一个公有变量与CBackgroundOptsPage属性页的radio 
  button建立关联,在主窗口中直接为其赋初始值,当用户单击属性表的OK按钮时在将其读出来。</P>
<p>如果用户点击OK按钮,DoModal()发挥IDOK,我们通知视图窗口使用新的图片和背景颜色。下面是几个屏幕截图显示几个不同的样式的视图:</p>
<P><IMG 
      height=152 alt=" [Alyson background - 11K] " 
      src="images/app_aly_t8.png" 
      width=131 align=bottom border=2> <IMG height=133 
      alt=" [Strong Bad background - 9K] " 
      src="images/sb_app_t8.png" 
      width=95 align=bottom border=2></P>
      
<H3><A name=bettersheet></A><font color="#FFFF66">创建一个更好的属性表类</font></H3>
      
<P>在OnOptions()中创建属性表是个好主意,但是在这里使用很多初始化代码却非常糟糕,这不是CMainFrame应该做得事情。更好的方法是从CPropertySheetImpl派生一个新类,在这个类中完成这些任务。</P>
<PRE><SPAN class=cpp-preprocessor><font color="#0000FF">#include "BackgroundOptsPage.h"</font></SPAN>
 
<font color="#0000FF"><SPAN class=cpp-keyword>class</SPAN> CAppPropertySheet : <SPAN class=cpp-keyword>public</SPAN> CPropertySheetImpl&lt;CAppPropertySheet&gt;
{
<SPAN class=cpp-keyword>public</SPAN>:
    <SPAN class=cpp-comment>// Construction</SPAN>
    CAppPropertySheet ( _U_STRINGorID title = (LPCTSTR) NULL, 
                        UINT uStartPage = <SPAN class=cpp-literal>0</SPAN>, HWND hWndParent = NULL );

    <SPAN class=cpp-comment>// Maps</SPAN>
    BEGIN_MSG_MAP(CAppPropertySheet)
        CHAIN_MSG_MAP(CPropertySheetImpl&lt;CAppPropertySheet&gt;)
    END_MSG_MAP()

    <SPAN class=cpp-comment>// Property pages</SPAN>
    CBackgroundOptsPage         m_pgBackground;
    CPropertyPage&lt;IDD_ABOUTBOX&gt; m_pgAbout;
};</font></PRE>
      
<P>我们使用这个类封装属性表中各个属性页的细节,将初始化代码移到属性表内部完成,构造函数完成添加页面,并设置其他必需的标志:</P>
<PRE><font color="#0000FF">CAppPropertySheet::CAppPropertySheet ( _U_STRINGorID title, UINT uStartPage, 
                                       HWND hWndParent ) :
    CPropertySheetImpl&lt;CAppPropertySheet&gt; ( title, uStartPage, hWndParent )
{
    m_psh.dwFlags |= PSH_NOAPPLYNOW;

    AddPage ( m_pgBackground );
    AddPage ( m_pgAbout );
}</font></PRE>
      
<P>这样一来,OnOptions()处理函数就变得简单了一些:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">void</font></SPAN><font color="#0000FF"> CMainFrame::OnOptions ( UINT uCode, <SPAN class=cpp-keyword>int</SPAN> nID, HWND hwndCtrl )
{
CAppPropertySheet sheet ( _T(<SPAN class=cpp-string>"PSheets Options"</SPAN>), <SPAN class=cpp-literal>0</SPAN> );

    sheet.m_pgBackground.m_nColor = m_view.m_nColor;
    sheet.m_pgBackground.m_nPicture = m_view.m_nPicture;

    <SPAN class=cpp-keyword>if</SPAN> ( IDOK == sheet.DoModal() )
        m_view.SetBackgroundOptions ( sheet.m_pgBackground.m_nColor,
                                      sheet.m_pgBackground.m_nPicture );
}</font></PRE>

<H2><A name=createwiz></A><font color="#FFFF66">创建一个向导样式的属性表</font></H2>
<P>创建一个向导和创建一个属性表很相似,这并不奇怪,只需稍做修改添加“上一步”和“下一步”按钮就行了。和MFC一样,你需要重载OnSetActive()函数并调用SetWizardButtons()使相应的按钮可用。我们先从一个简单的介绍页面开始,它的ID是IDD_WIZARD_INTRO:</P>
      
<P><IMG height=307 alt=" [Intro page - 3K] " 
      src="images/intropage8.png" 
      width=351 align=bottom border=0></P>
      
<P>注意这个页面没有标题栏文字,因为向导中的所有的页面通常都有相同的标题,我更愿意在CPropertySheetImpl的构造函数中设置这些文字,然后每个页面使用这个字符串资源。这就是为什么我只需要改变一个字符串就能改变所有页面标题文字的原因。</P>
<p>关于这个页面的实现代码在CWizIntroPage类中:</p>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CWizIntroPage : <SPAN class=cpp-keyword>public</SPAN> CPropertyPageImpl&lt;CWizIntroPage&gt;
{
<SPAN class=cpp-keyword>public</SPAN>:
    <SPAN class=cpp-keyword>enum</SPAN> { IDD = IDD_WIZARD_INTRO };

    <SPAN class=cpp-comment>// Construction</SPAN>
    CWizIntroPage();

    <SPAN class=cpp-comment>// Maps</SPAN>
    BEGIN_MSG_MAP(COptionsWizard)
        CHAIN_MSG_MAP(CPropertyPageImpl&lt;CWizIntroPage&gt;)
    END_MSG_MAP()

    <SPAN class=cpp-comment>// Notification handlers</SPAN>
    <SPAN class=cpp-keyword>int</SPAN> OnSetActive();
};</font></PRE>
      
<P>构造函数使用(引用)一个字符串资源ID来设置页面的文字:</P>
<PRE><font color="#0000FF">CWizIntroPage::CWizIntroPage() :
    CPropertyPageImpl&lt;CWizIntroPage&gt;( IDS_WIZARD_TITLE )
{
}</font></PRE>
      
<P>当这个页面激活时,字符串IDS_WIZARD_TITLE (&quot;PSheets Options Wizard&quot;)将出现在向导的标题栏。OnSetActive()仅仅使“下一步”按钮可用:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">int</font></SPAN><font color="#0000FF"> CWizIntroPage::OnSetActive()
{
    SetWizardButtons ( PSWIZB_NEXT );
    <SPAN class=cpp-keyword>return</SPAN> <SPAN class=cpp-literal>0</SPAN>;
}</font></PRE>
      
<P>为了实现一个向导,我们需要创建一个类COptionsWizard,还要在主窗口添加菜单Tools|Wizard。COptionsWizard类的构造函数和CAppPropertySheet类的构造函数一样,只是设置必要的样式标志和添加页面。</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> COptionsWizard : <SPAN class=cpp-keyword>public</SPAN> CPropertySheetImpl&lt;COptionsWizard&gt;
{
<SPAN class=cpp-keyword>public</SPAN>:
    <SPAN class=cpp-comment>// Construction</SPAN>
    COptionsWizard ( HWND hWndParent = NULL );

    <SPAN class=cpp-comment>// Maps</SPAN>
    BEGIN_MSG_MAP(COptionsWizard)
        CHAIN_MSG_MAP(CPropertySheetImpl&lt;COptionsWizard&gt;)
    END_MSG_MAP()

    <SPAN class=cpp-comment>// Property pages</SPAN>
    CWizIntroPage m_pgIntro;
};

COptionsWizard::COptionsWizard ( HWND hWndParent ) :
    CPropertySheetImpl&lt;COptionsWizard&gt; ( 0U, <SPAN class=cpp-literal>0</SPAN>, hWndParent )
{
    SetWizardMode();

    AddPage ( m_pgIntro );
}</font></PRE>
      
<P>CMainFrame类的Tools|Wizard菜单处理函数是这个样子:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">void</font></SPAN><font color="#0000FF"> CMainFrame::OnOptionsWizard ( UINT uCode, <SPAN class=cpp-keyword>int</SPAN> nID, HWND hwndCtrl )
{
COptionsWizard wizard;

    wizard.DoModal();
}</font></PRE>
      

⌨️ 快捷键说明

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