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

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

📁 MFC程序员的WTL指南,非常具体,WTL入门最好的书
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<P>这就是向导的效果:</P>
      
<P><IMG height=348 alt=" [Wizard on intro page - 6K] " 
      src="images/wizintro8.png" 
      width=441 align=bottom border=0></P>
      
<H3><A name=morepages></A><font color="#FFFF66">添加更多的属性页,使用DDV</font></H3>
      
<P>为了使这个向导能够有点用处,我们要为其添加一个设置视图背景颜色的页面。这个页面还将有一个checkbox演示如何处理DDV验证失败并阻止向导进行到下一页。下面就是新的页面,ID是IDD_WIZARD_BKCOLOR:</P>
      
<P><IMG height=304 alt=" [Color selection wizard page - 4K] " 
      src="images/colorpage8.png" 
      width=348 align=bottom border=0></P>
      
<P>这个类的实现代码在CWizBkColorPage类中,下面是相关的部分代码</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CWizBkColorPage :
    <SPAN class=cpp-keyword>public</SPAN> CPropertyPageImpl&lt;CWizBkColorPage&gt;,
    <SPAN class=cpp-keyword>public</SPAN> CWinDataExchange&lt;CWizBkColorPage&gt;
{
<SPAN class=cpp-keyword>public</SPAN>:
    <SPAN class=cpp-comment>// some stuff removed for brevity...</SPAN>

    BEGIN_DDX_MAP(CWizBkColorPage)
        DDX_RADIO(IDC_BLUE, m_nColor)
        DDX_CHECK(IDC_FAIL_DDV, m_bFailDDV)
    END_DDX_MAP()

    <SPAN class=cpp-comment>// Notification handlers</SPAN>
    <SPAN class=cpp-keyword>int</SPAN> OnSetActive();
    BOOL OnKillActive();

    <SPAN class=cpp-comment>// DDX vars</SPAN>
    <SPAN class=cpp-keyword>int</SPAN> m_nColor;

<SPAN class=cpp-keyword>protected</SPAN>:
    <SPAN class=cpp-keyword>int</SPAN> m_bFailDDV;
};</font></PRE>
      
<P>OnSetActive()的工作和前面的介绍页面相同,它使“上一步”和“下一步”按钮可用。OnKillActive()是个新的处理函数,它触发DDV,然后检查m_bFailDDV的值,如果是TRUE就表示checkbox处于选中状态,OnKillActive()将阻止向导进行到下一页。</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">int</font></SPAN><font color="#0000FF"> CWizBkColorPage::OnSetActive()
{
    SetWizardButtons ( PSWIZB_BACK | PSWIZB_NEXT );
    <SPAN class=cpp-keyword>return</SPAN> <SPAN class=cpp-literal>0</SPAN>;
}

<SPAN class=cpp-keyword>int</SPAN> CWizBkColorPage::OnKillActive()
{
    <SPAN class=cpp-keyword>if</SPAN> ( !DoDataExchange(<SPAN class=cpp-keyword>true</SPAN>) )
        <SPAN class=cpp-keyword>return</SPAN> TRUE;    <SPAN class=cpp-comment>// prevent deactivation</SPAN>

    <SPAN class=cpp-keyword>if</SPAN> ( m_bFailDDV )
        {
        MessageBox (
          _T(<SPAN class=cpp-string>"Error box checked, wizard will stay on this page."</SPAN>),
          _T(<SPAN class=cpp-string>"PSheets"</SPAN>), MB_ICONERROR );

        <SPAN class=cpp-keyword>return</SPAN> TRUE;    <SPAN class=cpp-comment>// prevent deactivation</SPAN>
        }

    <SPAN class=cpp-keyword>return</SPAN> FALSE;       <SPAN class=cpp-comment>// allow deactivation</SPAN>
}</font></PRE>
      
<P>需要注意的是OnKillActive()中做的事情也可以在OnWizardNext()中完成,因为这两个处理函数都可以使向导维持在当前页面。它们的不同之处在于OnKillActive()在用户单击“上一步”和“下一步”按钮时被调用,而OnWizardNext()只是在用户单击“下一步”按钮时被调用。OnWizardNext()还被用来完成其它目的,比如,它可以直接将向导引导到指定的页面而不是按顺序的下一页。</P>
<p>例子工程的向导还有另外两个页面,CWizBkPicturePage 和 CWizFinishPage,由于它们和前面的两个页面相似,我就不再详细介绍它们,想了解它们的细节可以查看源代码。</p>
<H2><A name=otherui></A><font color="#FFFF66">其他的界面考虑</font></H2>
<H3><A name=centersheet></A><font color="#FFFF66">置中一个属性表</font></H3>
      
<P>属性页和向导的默认位置是出现在父窗口的左上角:</P>
      
<P><IMG height=326 alt=" [sheet position - 8K] " 
      src="images/sheetpos8.png" 
      width=410 align=bottom border=0></P>
      
<P>这看起来有点不爽,还好有方法可以补救。第一种方法是重载CPropertySheetImpl::PropSheetCallback()函数,在这个函数中将属性表置中。PropSheetCallback()是MSDN中介绍的PropSheetProc()的回调函数,操作系统在属性表创建时调用这个函数,WTL也是利用这个时间子类化属性表窗口的。所以我们的第一种尝试是:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CAppPropertySheet : <SPAN class=cpp-keyword>public</SPAN> CPropertySheetImpl&lt;CAppPropertySheet&gt;
{
<SPAN class=cpp-comment>//...</SPAN>
    <SPAN class=cpp-keyword>static</SPAN> <SPAN class=cpp-keyword>int</SPAN> CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam)
    {
    <SPAN class=cpp-keyword>int</SPAN> nRet = CPropertySheetImpl&lt;CAppPropertySheet&gt;::PropSheetCallback (
                                                        hWnd, uMsg, lParam );
 
        <SPAN class=cpp-keyword>if</SPAN> ( PSCB_INITIALIZED == uMsg )
            {
            <SPAN class=cpp-comment>// center sheet... somehow?</SPAN>
            }
 
        <SPAN class=cpp-keyword>return</SPAN> nRet;
    }
};</font></PRE>
<P>正如你看到的,我们遇到了棘手的问题。PropSheetCallback()是一个静态方法,不能使用this指针访问属性表窗口。那将这些代码从CPropertySheetImpl::PropSheetCallback()中拷贝出来,然后添加我们自己的方法行不行呢?撇开刚才将代码和特定版本的WTL联系在一起的方法(这已经被证明不是各好方法),现在代码应该是这样的:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CAppPropertySheet : <SPAN class=cpp-keyword>public</SPAN> CPropertySheetImpl&lt;CAppPropertySheet&gt;
{
<SPAN class=cpp-comment>//...</SPAN>
    <SPAN class=cpp-keyword>static</SPAN> <SPAN class=cpp-keyword>int</SPAN> CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM)
    {
        <SPAN class=cpp-keyword>if</SPAN>(uMsg == PSCB_INITIALIZED)
        {
            <B><SPAN class=cpp-comment>// Code copied from WTL and tweaked to use CAppPropertySheet</SPAN>
            <SPAN class=cpp-comment>// instead of T:</span></B>
            ATLASSERT(hWnd != NULL);
            CAppPropertySheet* pT = (CAppPropertySheet*)
                                        _Module.ExtractCreateWndData();
            <SPAN class=cpp-comment>// subclass the sheet window</SPAN>
            pT-&gt;SubclassWindow(hWnd);
            <SPAN class=cpp-comment>// remove page handles array</SPAN>
            pT-&gt;_CleanUpPages();
 
            <B><SPAN class=cpp-comment>// Our own code follows:</SPAN>
            pT-&gt;CenterWindow ( pT-&gt;m_psh.hwndParent );</B>
        }
 
        <SPAN class=cpp-keyword>return</SPAN> <SPAN class=cpp-literal>0</SPAN>;
    }
};</font></PRE>
<P>这从理论上讲很完美,但是我试过,属性表的位置并未改变。显然,通用控件的代码在我们调用CenterWindow()之后又改变了属性表窗口的位置。</P>
<p>必须放弃这个将代码封装到属性表类的方法,尽管它是个好的解决方案。我又回到原来的方案,即使用属性页窗口和属性表窗口相互协作是属性表窗口置中。我添加了一个用户定义消息UWM_CENTER_SHEET:</p>
<PRE><SPAN class=cpp-preprocessor><font color="#0000FF">  #define UWM_CENTER_SHEET WM_APP</font></SPAN></PRE>
      
<P>CAppPropertySheet 在它的消息映射链中处理这个消息:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CAppPropertySheet : <SPAN class=cpp-keyword>public</SPAN> CPropertySheetImpl&lt;CAppPropertySheet&gt;
{
<SPAN class=cpp-comment>//...</SPAN>
    BEGIN_MSG_MAP(CAppPropertySheet)
        MESSAGE_HANDLER_EX(UWM_CENTER_SHEET, OnPageInit)
        CHAIN_MSG_MAP(CPropertySheetImpl&lt;CAppPropertySheet&gt;)
    END_MSG_MAP()
 
    <SPAN class=cpp-comment>// Message handlers</SPAN>
    LRESULT OnPageInit ( UINT, WPARAM, LPARAM );
 
<SPAN class=cpp-keyword>protected</SPAN>:
    <SPAN class=cpp-keyword>bool</SPAN> m_bCentered;  <SPAN class=cpp-comment>// set to false in the ctor</SPAN>
};
 
LRESULT CAppPropertySheet::OnPageInit ( UINT, WPARAM, LPARAM )
{
    <SPAN class=cpp-keyword>if</SPAN> ( !m_bCentered )
        {
        m_bCentered = <SPAN class=cpp-keyword>true</SPAN>;
        CenterWindow ( m_psh.hwndParent );
        }
 
    <SPAN class=cpp-keyword>return</SPAN> <SPAN class=cpp-literal>0</SPAN>;
}</font></PRE>
      
<P>然后,每个属性页的OnInitDialog() 方法发送这个消息到属性表窗口:</P>
<PRE><font color="#0000FF">BOOL CBackgroundOptsPage::OnInitDialog ( HWND hwndFocus, LPARAM lParam )
{
    <B>GetPropertySheet().SendMessage ( UWM_CENTER_SHEET );</B>
 
    DoDataExchange(<SPAN class=cpp-keyword>false</SPAN>);
    <SPAN class=cpp-keyword>return</SPAN> TRUE;
}</font></PRE>
<P>添加m_bCentered标志确保属性表窗口只响应收到的第一个UWM_CENTER_SHEET消息。</P>
      
<H3><A name=addicons></A><font color="#FFFF66">在属性页中添加图标</font> </H3>
      
<P>如果要使用属性表和属性页的未被成员函数封装的特性,就需要直接访问相关的数据结构:CPropertySheetImpl类中的PROPSHEETHEADER类型(结构)成员m_psh和CPropertyPageImpl类中的PROPSHEETPAGE类型(结构)成员m_psp。</P>
<p>例如:为例子中Option属性表中的Background页面添加一个图标,就需要添加一个标志并设置属性页的PROPSHEETPAGE结构中的几个成员:</p>
<PRE><font color="#0000FF">CBackgroundOptsPage::CBackgroundOptsPage()
{
    m_psp.dwFlags |= PSP_USEICONID;
    m_psp.pszIcon = MAKEINTRESOURCE(IDI_TABICON);
    m_psp.hInstance = _Module.GetResourceInstance();
}</font></PRE>
      
<P>下面是这些代码的效果:</P>
      
<P><IMG height=67 alt=" [Tab icon - 2K] " 
      src="images/tabicon8.png" 
      width=163 align=bottom border=0></P>
 
<H2><A name=upnext></A><font color="#FFFF66">继续</font></H2>
      
<P>我将在第九章介绍WTL的一些工具类,还有GDI对象和通用对话框的包装类。</P>

<H2><A name=revisionhistory></A><font color="#FFFF66">修改记录</font></H2>
      
<P>September 13, 2003: 文章第一次发布。
<P>&nbsp;
</body>
</html>

⌨️ 快捷键说明

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