📄 wtl for mfc programmers, part iv - dialogs and controls - wtl.htm
字号:
<SPAN class=cpp-keyword>return</SPAN> TRUE;
}</font></PRE>
<P>下面是新的WM_SETCURSOR消息处理函数:</P>
<PRE><font color="#0033FF">LRESULT CMainDlg::OnSetCursor_OK (HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg )
{
<SPAN class=cpp-keyword>static</SPAN> HCURSOR hcur = LoadCursor ( NULL, IDC_HAND );
<SPAN class=cpp-keyword>if</SPAN> ( NULL != hcur )
{
SetCursor ( hcur );
<SPAN class=cpp-keyword>return</SPAN> TRUE;
}
<SPAN class=cpp-keyword>else</SPAN>
{
SetMsgHandled(<SPAN class=cpp-keyword>false</SPAN>);
<SPAN class=cpp-keyword>return</SPAN> FALSE;
}
}
LRESULT CMainDlg::OnSetCursor_Exit ( HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg )
{
<SPAN class=cpp-keyword>static</SPAN> HCURSOR hcur = LoadCursor ( NULL, IDC_NO );
<SPAN class=cpp-keyword>if</SPAN> ( NULL != hcur )
{
SetCursor ( hcur );
<SPAN class=cpp-keyword>return</SPAN> TRUE;
}
<SPAN class=cpp-keyword>else</SPAN>
{
SetMsgHandled(<SPAN class=cpp-keyword>false</SPAN>);
<SPAN class=cpp-keyword>return</SPAN> FALSE;
}
}</font></PRE>
<P>如果你还想使用按钮类的特性,你需要这样声明变量:</P>
<PRE> <font color="#0033FF">CContainedWindowT<CButton> m_wndOKBtn;</font></PRE>
<P>这样就可以使用CButton类的方法。</P>
<p>当你把鼠标光标移到这些按钮上就可以看到WM_SETCURSOR消息处理函数的作用结果:</p>
<P><IMG height=220 alt=" [OK button cursor - 5K] "
src="images/okbtncur4.png"
width=333 align=bottom border=0> <IMG height=220
alt=" [Exit button cursor - 5K] "
src="images/exitbtncur4.png"
width=333 align=bottom border=0></P>
<H3><A name=atl3></A><font color="#FFFF66">ATL 方式 3 - 子类化(Subclassing)</font></H3>
<P>第三种方法创建一个CWindowImpl派生类并用它子类化一个控件。这和第二种方法有些相似,只是消息处理放在CWindowImpl类内部而不是对话框类中。</P>
<p>ControlMania1使用这种方法子类化主对话框的About按钮。下面是CButtonImpl类,他从CWindowImpl类派生,处理WM_SETCURSOR消息:</p>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CButtonImpl : <SPAN class=cpp-keyword>public</SPAN> CWindowImpl<CButtonImpl, CButton>
{
BEGIN_MSG_MAP_EX(CButtonImpl)
MSG_WM_SETCURSOR(OnSetCursor)
END_MSG_MAP()
LRESULT OnSetCursor(HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg)
{
<SPAN class=cpp-keyword>static</SPAN> HCURSOR hcur = LoadCursor ( NULL, IDC_SIZEALL );
<SPAN class=cpp-keyword>if</SPAN> ( NULL != hcur )
{
SetCursor ( hcur );
<SPAN class=cpp-keyword>return</SPAN> TRUE;
}
<SPAN class=cpp-keyword>else</SPAN>
{
SetMsgHandled(<SPAN class=cpp-keyword>false</SPAN>);
<SPAN class=cpp-keyword>return</SPAN> FALSE;
}
}
};</font></PRE>
<P>接着在主对话框声明一个CButtonImpl成员变量:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CMainDlg : <SPAN class=cpp-keyword>public</SPAN> CDialogImpl<CMainDlg>
{
<SPAN class=cpp-comment>// ...</SPAN>
<SPAN class=cpp-keyword>protected</SPAN>:
CContainedWindow m_wndOKBtn, m_wndExitBtn;
<B>CButtonImpl m_wndAboutBtn;</B>
};</font></PRE>
<P>最后,在OnInitDialog()种子类化About按钮。</P>
<PRE><font color="#0033FF">LRESULT CMainDlg::OnInitDialog(...)
{
<SPAN class=cpp-comment>// ...</SPAN>
<SPAN class=cpp-comment>// Attach CContainedWindows to OK and Exit buttons</SPAN>
m_wndOKBtn.SubclassWindow ( GetDlgItem(IDOK) );
m_wndExitBtn.SubclassWindow ( GetDlgItem(IDCANCEL) );
<B><SPAN class=cpp-comment>// CButtonImpl: subclass the About button</SPAN>
m_wndAboutBtn.SubclassWindow ( GetDlgItem(ID_APP_ABOUT) );</B>
<SPAN class=cpp-keyword>return</SPAN> TRUE;
}</font></PRE>
<H3><A name=wtlway></A><font color="#FFFF66">WTL 方式 - 对话框数据交换(DDX)</font></H3>
<P>WTL的DDX(对话框数据交换)很像MFC,可以使用很简单的方法将变量和控件关联起来。首先,和前面的例子一样你需要从CWindowImpl派生一个新类,这次我们使用一个新类CEditImpl,因为这次我们使用得是Edit控件。你还需要将#include
atlddx.h 添加到stdafx.h中,这样就可以使用DDX代码。</P>
<p>要使主对话框支持DDX,需要将CWinDataExchange添加到继承列表中:</p>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CMainDlg : <SPAN class=cpp-keyword>public</SPAN> CDialogImpl<CMainDlg>,
<B><SPAN class=cpp-keyword>public</SPAN> CWinDataExchange<CMainDlg></B>
{
<SPAN class=cpp-comment>//...</SPAN>
};</font></PRE>
<P>接着在对话框类中添加DDX链,这和MFC的类向导使用的DoDataExchange()函数功能相似。对于不同类型的数据可以使用不同的DDX宏,我们使用DDX_CONTROL用来连接变量和控件,这次我们使用CEditImpl处理WM_CONTEXTMENU消息,使它能够在你右键单控件时做一些事情。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CEditImpl : <SPAN class=cpp-keyword>public</SPAN> CWindowImpl<CEditImpl, CEdit>
{
BEGIN_MSG_MAP_EX(CEditImpl)
MSG_WM_CONTEXTMENU(OnContextMenu)
END_MSG_MAP()
<SPAN class=cpp-keyword>void</SPAN> OnContextMenu ( HWND hwndCtrl, CPoint ptClick )
{
MessageBox(<SPAN class=cpp-string>"Edit control handled WM_CONTEXTMENU"</SPAN>);
}
};
<SPAN class=cpp-keyword>class</SPAN> CMainDlg : <SPAN class=cpp-keyword>public</SPAN> CDialogImpl<CMainDlg>,
<SPAN class=cpp-keyword>public</SPAN> CWinDataExchange<CMainDlg>
{
<SPAN class=cpp-comment>//...</SPAN>
<B>BEGIN_DDX_MAP(CMainDlg)
DDX_CONTROL(IDC_EDIT, m_wndEdit)
END_DDX_MAP()</B>
<SPAN class=cpp-keyword>protected</SPAN>:
CContainedWindow m_wndOKBtn, m_wndExitBtn;
CButtonImpl m_wndAboutBtn;<B>
</B> <B>CEditImpl m_wndEdit;</B>
};</font></PRE>
<P>最后,在OnInitDialog()中调用DoDataExchange()函数,这个函数是继承自CWinDataExchange。DoDataExchange()第一次被调用时完成相关控件的子类化工作,所以在这个例子中,DoDataExchange()子类化ID为IDC_EDIT的控件,将其与m_wndEdit建立关联。</P>
<PRE><font color="#0033FF">LRESULT CMainDlg::OnInitDialog(...)
{
<SPAN class=cpp-comment>// ...</SPAN>
<SPAN class=cpp-comment>// Attach CContainedWindows to OK and Exit buttons</SPAN>
m_wndOKBtn.SubclassWindow ( GetDlgItem(IDOK) );
m_wndExitBtn.SubclassWindow ( GetDlgItem(IDCANCEL) );
<SPAN class=cpp-comment>// CButtonImpl: subclass the About button</SPAN>
m_wndAboutBtn.SubclassWindow ( GetDlgItem(ID_APP_ABOUT) );
<B><SPAN class=cpp-comment>// First DDX call, hooks up variables to controls.</span></B>
<B>DoDataExchange(<SPAN class=cpp-keyword>false</SPAN>);</B>
<SPAN class=cpp-keyword>return</SPAN> TRUE;
}</font></PRE>
<P>DoDataExchange()的参数与MFC的UpdateData()函数的参数意义相同,我会在下一节详细介绍。</P>
<p>现在运行ControlMania1程序,可以看到子类化的效果。鼠标右键单击编辑框将弹出消息框,当鼠标通过按钮上时鼠标形状会改变。</p>
<H2><A name=moreddx></A><font color="#FFFF66">DDX的详细内容</font></H2>
<P>当然,DDX是用来做数据交换的,WTL支持在Edit控件和字符串之间交换数据,也可以将字符串解析成数字,转换成整型或浮点型变量,还支持Check box和Radio
button组的状态与int型变量之间的转换。</P>
<H3><A name=ddxmacros></A><font color="#FFFF66">DDX 宏</font></H3>
<P>DDX可以使用6种宏,每一种宏都对应一个CWinDataExchange类的方法支持其工作,每一种宏都用相同的形式:DDX_FOO(控件ID, 变量),每一种宏都可以支持多种类型的变量,例如DDX_TEXT的重载就支持多种类型的数据。</P>
<DL>
<DT>DDX_TEXT
<DD>在字符串和edit box控件之间传输数据,变量类型可以是CString, BSTR, CComBSTR或者静态分配的字符串数组,但是不能使用new动态分配的数组。
<DT>DDX_INT
<DD>在edit box控件和数字变量之间传输int型数据。
<DT>DDX_UINT
<DD>在edit box控件和数字变量之间传输无符号int型数据。
<DT>DDX_FLOAT
<DD>在edit box控件和数字变量之间传输浮点型(float)数据或双精度型数据(double)。
<DT>DDX_CHECK
<DD>在check box控件和int型变量之间转换check box控件的状态。
<DT>DDX_RADIO
<DD>在radio buttons控件组和int型变量之间转换radio buttons控件组的状态。</DD>
</DL>
<P>DDX_FLOAT宏有一些特殊,要使用DDX_FLOAT宏需要在stdafx.h文件的所有WTL头文件包含之前添加一行定义:</P>
<PRE><SPAN class=cpp-preprocessor><font color="#0033FF">#define _ATL_USE_DDX_FLOAT</font></SPAN></PRE>
<P>这个定义是必要的,因为默认状态为了优化程序的大小而不支持浮点数。</P>
<H3><A name=moreddx></A><font color="#FFFF66">有关 DoDataExchange()的详细内容</font></H3>
<P>调用DoDataExchange()方法和在MFC中使用UpdateData()一样,DoDataExchange()的函数原型是:</P>
<PRE><font color="#0033FF">BOOL DoDataExchange ( BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-<SPAN class=cpp-literal>1</SPAN> );</font></PRE>
<P>参数:</P>
<DL>
<DT>bSaveAndValidate
<DD>指示数据传输方向的标志。TRUE表示将数据从控件传输给变量,FALSE表示将数据从变量传输给控件。需要注意得是这个参数的默认值是FALSE,而MFC的UpdateData()函数的默认值是TRUE。为了方便记忆,你可以使用DDX_SAVE
和 DDX_LOAD标号(它们分别被定义为TRUE和FALSE)。
<DT>nCtlID
<DD>使用-1可以更新所有控件,如果只想DDX宏作用于一个控件就使用控件的ID。</DD>
</DL>
<P>如果控件更新成功DoDataExchange()会返回TRUE,如果失败就返回FALSE,对话框类有两个重载函数处理数据交换错误。一个是OnDataExchangeError(),无论什么原因的错误都会调用这个函数,这个函数的默认实现在CWinDataExchange中,它仅仅是驱动PC喇叭发出一声蜂鸣并将出错的控件设为当前焦点。另一个函数是OnDataValidateError(),但是要到本文的第五章介绍DDV时才用得到。</P>
<H3><A name=usingddx></A><font color="#FFFF66">使用DDX</font></H3>
<P>在CMainDlg中添加几个变量,演示DDX的使用方法。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CMainDlg : <SPAN class=cpp-keyword>public</SPAN> ...
{
<SPAN class=cpp-comment>//...</SPAN>
BEGIN_DDX_MAP(CMainDlg)
DDX_CONTROL(IDC_EDIT, m_wndEdit)
<B>DDX_TEXT(IDC_EDIT, m_sEditContents)
DDX_INT(IDC_EDIT, m_nEditNumber)</B>
END_DDX_MAP()
<SPAN class=cpp-keyword>protected</SPAN>:
<SPAN class=cpp-comment>// DDX variables</SPAN>
CString m_sEditContents;
<SPAN class=cpp-keyword>int</SPAN> m_nEditNumber;
};</font></PRE>
<P>在OK按钮的处理函数中,我们首先调用DoDataExchange()将将edit控件的数据传送给我们刚刚添加的两个变量,然后将结果显示在列表控件中。</P>
<PRE><font color="#0033FF">LRESULT CMainDlg::OnOK ( UINT uCode, <SPAN class=cpp-keyword>int</SPAN> nID, HWND hWndCtl )
{
CString str;
<SPAN class=cpp-comment>// Transfer data from the controls to member variables.</SPAN>
<SPAN class=cpp-keyword>if</SPAN> ( !DoDataExchange(<SPAN class=cpp-keyword>true</SPAN>) )
<SPAN class=cpp-keyword>return</SPAN>;
m_wndList.DeleteAllItems();
m_wndList.InsertItem ( <SPAN class=cpp-literal>0</SPAN>, _T(<SPAN class=cpp-string>"DDX_TEXT"</SPAN>) );
m_wndList.SetItemText ( <SPAN class=cpp-literal>0</SPAN>, <SPAN class=cpp-literal>1</SPAN>, m_sEditContents );
str.Format ( _T(<SPAN class=cpp-string>"%d"</SPAN>), m_nEditNumber );
m_wndList.InsertItem ( <SPAN class=cpp-literal>1</SPAN>, _T(<SPAN class=cpp-string>"DDX_INT"</SPAN>) );
m_wndList.SetItemText ( <SPAN class=cpp-literal>1</SPAN>, <SPAN class=cpp-literal>1</SPAN>, str );
}</font></PRE>
<P><IMG height=246 alt=" [DDX results - 5K] "
src="images/ddxok4.png"
width=333 align=bottom border=0></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -