?? wtl for mfc programmers, part iv.mht
字號:
<P>The parameters are:</P>
<DL>
<DT><CODE>bSaveAndValidate</CODE>=20
<DD>Flag indicating which direction the data will be =
transferred.=20
Passing <CODE>TRUE</CODE> transfers from the controls to the =
variables.=20
Passing <CODE>FALSE</CODE> transfers from the variables to the =
controls.=20
Note that the default for this parameter is <CODE>FALSE</CODE>, =
while=20
the default for MFC's <CODE>UpdateData()</CODE> is =
<CODE>TRUE</CODE>.=20
You can also use the symbols <CODE>DDX_SAVE</CODE> and=20
<CODE>DDX_LOAD</CODE> (defined as <CODE>TRUE</CODE> and=20
<CODE>FALSE</CODE> respectively) as the parameter, if you find =
that=20
easier to remember.=20
<DT><CODE>nCtlID</CODE>=20
<DD>Pass -1 to update all controls. Otherwise, if you want to =
use DDX on=20
only one control, pass the control's ID. </DD></DL>
<P><CODE>DoDataExchange()</CODE> returns <CODE>TRUE</CODE> if the =
controls=20
are updated successfully, or <CODE>FALSE</CODE> if not. There are =
two=20
functions you can override in your dialog to handle errors. The =
first,=20
<CODE>OnDataExchangeError()</CODE> is called if the data exchange =
fails=20
for any reason. The default implementation in=20
<CODE>CWinDataExchange</CODE> sounds a beep and sets focus to the =
control=20
that caused the error. The other function is=20
<CODE>OnDataValidateError()</CODE>, but we'll get to that in Part =
V when=20
we cover DDV.</P>
<H3><A name=3Dusingddx></A>Using DDX</H3>
<P>Let's add a couple of variables to <CODE>CMainDlg</CODE> for =
use with=20
DDX.</P><PRE><SPAN class=3Dcpp-keyword>class</SPAN> CMainDlg : =
<SPAN class=3Dcpp-keyword>public</SPAN> ...
{
<SPAN class=3Dcpp-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()
=20
<SPAN class=3Dcpp-keyword>protected</SPAN>:
<SPAN class=3Dcpp-comment>// DDX variables</SPAN>
CString m_sEditContents;
<SPAN class=3Dcpp-keyword>int</SPAN> m_nEditNumber;
};</PRE>
<P>In the <I>OK</I> button handler, we first call=20
<CODE>DoDataExchange()</CODE> to transfer the data from the edit =
control=20
to the two variables we just added. We then show the results in =
the list=20
control.</P><PRE>LRESULT CMainDlg::OnOK ( UINT uCode, <SPAN =
class=3Dcpp-keyword>int</SPAN> nID, HWND hWndCtl )
{
CString str;
=20
<SPAN class=3Dcpp-comment>// Transfer data from the controls to =
member variables.</SPAN>
<SPAN class=3Dcpp-keyword>if</SPAN> ( !DoDataExchange(<SPAN =
class=3Dcpp-keyword>true</SPAN>) )
<SPAN class=3Dcpp-keyword>return</SPAN>;
=20
m_wndList.DeleteAllItems();
=20
m_wndList.InsertItem ( <SPAN class=3Dcpp-literal>0</SPAN>, _T(<SPAN =
class=3Dcpp-string>"DDX_TEXT"</SPAN>) );
m_wndList.SetItemText ( <SPAN class=3Dcpp-literal>0</SPAN>, <SPAN =
class=3Dcpp-literal>1</SPAN>, m_sEditContents );
=20
str.Format ( _T(<SPAN class=3Dcpp-string>"%d"</SPAN>), m_nEditNumber =
);
m_wndList.InsertItem ( <SPAN class=3Dcpp-literal>1</SPAN>, _T(<SPAN =
class=3Dcpp-string>"DDX_INT"</SPAN>) );
m_wndList.SetItemText ( <SPAN class=3Dcpp-literal>1</SPAN>, <SPAN =
class=3Dcpp-literal>1</SPAN>, str );
}</PRE>
<P><IMG height=3D246 alt=3D" [DDX results - 5K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC4/ddxok.png" =
width=3D333=20
align=3Dbottom border=3D0></P>
<P>If you enter non-numerical text in the edit box, =
<CODE>DDX_INT</CODE>=20
will fail and call <CODE>OnDataExchangeError()</CODE>.=20
<CODE>CMainDlg</CODE> overrides <CODE>OnDataExchangeError()</CODE> =
to show=20
a message box:</P><PRE><SPAN class=3Dcpp-keyword>void</SPAN> =
CMainDlg::OnDataExchangeError ( UINT nCtrlID, BOOL bSave )
{
CString str;
=20
str.Format ( _T(<SPAN class=3Dcpp-string>"DDX error during exchange =
with control: %u"</SPAN>), nCtrlID );
MessageBox ( str, _T(<SPAN =
class=3Dcpp-string>"ControlMania1"</SPAN>), MB_ICONWARNING );
=20
::SetFocus ( GetDlgItem(nCtrlID) );
}</PRE>
<P><IMG height=3D338 alt=3D" [DDX error msg - 7K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC4/ddxerror.png" =
width=3D372=20
align=3Dbottom border=3D0></P>
<P>As our last DDX example, let's add a check box to show the =
usage of=20
<CODE>DDX_CHECK</CODE>:</P>
<P><IMG height=3D265 alt=3D" [Msg checkbox - 6K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC4/msgchkbox.png" =
width=3D333=20
align=3Dbottom border=3D0></P>
<P>The variable used with <CODE>DDX_CHECK</CODE> is an <CODE><SPAN =
class=3Dcpp-keyword>int</SPAN></CODE>, and can have the value 0, =
1, or 2,=20
corresponding to the unchecked, checked, or indeterminate states. =
You can=20
also use the constants <CODE>BST_UNCHECKED</CODE>,=20
<CODE>BST_CHECKED</CODE>, and <CODE>BST_INDETERMINATE</CODE> =
instead of=20
0-2. For check boxes that can only be checked or unchecked, you =
can treat=20
the variable like a boolean.</P>
<P>Here are the changes to make hook up the check box to =
DDX:</P><PRE><SPAN class=3Dcpp-keyword>class</SPAN> CMainDlg : <SPAN =
class=3Dcpp-keyword>public</SPAN> ...
{
<SPAN class=3Dcpp-comment>//...</SPAN>
BEGIN_DDX_MAP(CMainDlg)
DDX_CONTROL(IDC_EDIT, m_wndEdit)
DDX_TEXT(IDC_EDIT, m_sEditContents)
DDX_INT(IDC_EDIT, m_nEditNumber)
<B>DDX_CHECK(IDC_SHOW_MSG, m_nShowMsg)</B>
END_DDX_MAP()
=20
<SPAN class=3Dcpp-keyword>protected</SPAN>:
<SPAN class=3Dcpp-comment>// DDX variables</SPAN>
CString m_sEditContents;
<SPAN class=3Dcpp-keyword>int</SPAN> m_nEditNumber;
<B><SPAN class=3Dcpp-keyword>int</SPAN> m_nShowMsg;</B>
};</PRE>
<P>At the end of <CODE>OnOK()</CODE>, we test =
<CODE>m_nShowMsg</CODE> to=20
see if the check box was checked.</P><PRE><SPAN =
class=3Dcpp-keyword>void</SPAN> CMainDlg::OnOK ( UINT uCode, <SPAN =
class=3Dcpp-keyword>int</SPAN> nID, HWND hWndCtl )
{
<SPAN class=3Dcpp-comment>// Transfer data from the controls to =
member variables.</SPAN>
<SPAN class=3Dcpp-keyword>if</SPAN> ( !DoDataExchange(<SPAN =
class=3Dcpp-keyword>true</SPAN>) )
<SPAN class=3Dcpp-keyword>return</SPAN>;
<SPAN class=3Dcpp-comment>//...</SPAN>
<SPAN class=3Dcpp-keyword>if</SPAN> ( m_nShowMsg )
MessageBox ( _T(<SPAN class=3Dcpp-string>"DDX =
complete!"</SPAN>), _T(<SPAN class=3Dcpp-string>"ControlMania1"</SPAN>), =
MB_ICONINFORMATION );
}</PRE>
<P>The sample project has examples of using the other =
<CODE>DDX_*</CODE>=20
macros as well.</P>
<H2><A name=3Dhandlenotify></A>Handling Notifications from =
Controls</H2>
<P>Handling notifications in WTL is similar to API-level =
programming. A=20
control sends its parent a notification in the form of a=20
<CODE>WM_COMMAND</CODE> or <CODE>WM_NOTIFY</CODE> message, and =
it's the=20
parent's responsibility to handle it. A few other messages can be=20
considered notifications, such as <CODE>WM_DRAWITEM</CODE>, which =
is sent=20
when an owner-drawn control needs to be painted. The parent window =
can act=20
on the message itself, or it can <I>reflect</I> the message back =
to the=20
control. Reflection works as in MFC - the control is able to =
handle=20
notifications itself, making the code self-contained and easier to =
move to=20
other projects.</P>
<H3><A name=3Dhandleinparent></A>Handing notifications in the =
parent</H3>
<P>Notifications sent as <CODE>WM_NOTIFY</CODE> and=20
<CODE>WM_COMMAND</CODE> contain various information. The =
parameters in a=20
<CODE>WM_COMMAND</CODE> message contain the ID of the control =
sending the=20
message, the <CODE>HWND</CODE> of the control, and the =
notification code.=20
<CODE>WM_NOTIFY</CODE> messages have all those as well as a =
pointer to an=20
<CODE>NMHDR</CODE> data structure. ATL and WTL have various =
message map=20
macros for handling these notifications. I'll only be covering the =
WTL=20
macros here, since this is a WTL article after all. Note that for =
all of=20
these macros, you need to use <CODE>BEGIN_MSG_MAP_EX</CODE> in =
your=20
message map, and #include atlcrack.h.</P>
<H4>Message map macros</H4>
<P>To handle a <CODE>WM_COMMAND</CODE> notification, use one of =
the=20
<CODE>COMMAND_HANDLER_EX</CODE> macros:</P>
<DL>
<DT><CODE>COMMAND_HANDLER_EX(id, code, func)</CODE>=20
<DD>Handles a notification from a particular control with a =
particular=20
code.=20
<DT><CODE>COMMAND_ID_HANDLER_EX(id, func)</CODE>=20
<DD>Handles all notifications with a particular code, regardless =
of=20
which control sends them.=20
<DT><CODE>COMMAND_CODE_HANDLER_EX(code, func)</CODE>=20
<DD>Handles all notifications from a particular control, =
regardless of=20
the code.=20
<DT><CODE>COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func)</CODE> =
<DD>Handles all notifications from controls whose IDs are in the =
range=20
idFirst to idLast inclusive, regardless of the code.=20
<DT><CODE>COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code,=20
func)</CODE>=20
<DD>Handles all notifications from controls whose IDs are in the =
range=20
idFirst to idLast inclusive, with a particular code. </DD></DL>
<P>Examples:</P>
<UL>
<LI><CODE>COMMAND_HANDLER_EX(IDC_USERNAME, EN_CHANGE,=20
OnUsernameChange)</CODE>: Handles <CODE>EN_CHANGE</CODE> when =
sent from=20
the edit box with ID IDC_USERNAME.=20
<LI><CODE>COMMAND_ID_HANDLER_EX(IDOK, OnOK)</CODE>: Handles all=20
notifications sent from the control with ID IDOK.=20
<LI><CODE>COMMAND_RANGE_CODE_HANDLER_EX(IDC_MONDAY, IDC_FRIDAY,=20
BN_CLICKED, OnDayClicked)</CODE>: Handles all =
<CODE>BN_CLICKED</CODE>=20
notifications from the controls with IDs in the range IDC_MONDAY =
to=20
IDC_FRIDAY </LI></UL>
<P>There are also macros for handling <CODE>WM_NOTIFY</CODE> =
messages.=20
They work just like the macros above, but their names start with=20
"<CODE>NOTIFY_</CODE>" instead of "<CODE>COMMAND_</CODE>".</P>
<P>The prototype for a <CODE>WM_COMMAND</CODE> handler =
is:</P><PRE> <SPAN class=3Dcpp-keyword>void</SPAN> func ( UINT uCode, =
<SPAN class=3Dcpp-keyword>int</SPAN> nCtrlID, HWND hwndCtrl );</PRE>
<P><CODE>WM_COMMAND</CODE> notifications do not use a return =
value, so=20
handlers don't either. The prototype for a <CODE>WM_NOTIFY</CODE> =
handler=20
is:</P><PRE> LRESULT func ( NMHDR* phdr );</PRE>
<P>The return value of the handler is used as the message result. =
This is=20
different from MFC, where the handler receives an =
<CODE>LRESULT*</CODE>=20
and sets the message result through that variable. The =
notification code=20
and the <CODE>HWND</CODE> of the control that sent the =
notification are=20
available in the <CODE>NMHDR</CODE> struct, as the =
<CODE>code</CODE> and=20
<CODE>hwndFrom</CODE> members. Just as in MFC, if the notification =
sends a=20
struct that is not a plain <CODE>NMHDR</CODE>, your handler should =
cast=20
the <CODE>phdr</CODE> parameter to the correct type.</P>
<P>We'll add a notification handler to <CODE>CMainDlg</CODE> that =
handles=20
<CODE>LVN_ITEMCHANGED</CODE> sent from the list control, and shows =
the=20
currently-selected item in the dialog. We start by adding the =
message map=20
macro and message handler:</P><PRE><SPAN =
class=3Dcpp-keyword>class</SPAN> CMainDlg : <SPAN =
class=3Dcpp-keyword>public</SPAN> ...
{
BEGIN_MSG_MAP_EX(CMainDlg)
NOTIFY_HANDLER_EX(IDC_LIST, LVN_ITEMCHANGED, OnListItemchanged)
END_MSG_MAP()
=20
LRESULT OnListItemchanged(NMHDR* phdr);
<SPAN class=3Dcpp-comment>//...</SPAN>
};</PRE>
<P>Here's the message handler:</P><PRE>LRESULT =
CMainDlg::OnListItemchanged ( NMHDR* phdr )
{
NMLISTVIEW* pnmlv =3D (NMLISTVIEW*) phdr;
<SPAN class=3Dcpp-keyword>int</SPAN> nSelItem =3D =
m_wndList.GetSelectedIndex();
CString sMsg;
=20
<SPAN class=3Dcpp-comment>// If no item is selected, show "none". =
Otherwise, show its index.</SPAN>
<SPAN class=3Dcpp-keyword>if</SPAN> ( -<SPAN =
class=3Dcpp-literal>1</SPAN> =3D=3D nSelItem )
sMsg =3D _T(<SPAN class=3Dcpp-string>"(none)"</SPAN>);
<SPAN class=3Dcpp-keyword>else</SPAN>
sMsg.Format ( _T(<SPAN class=3Dcpp-string>"%d"</SPAN>), nSelItem =
);
=20
SetDlgItemText ( IDC_SEL_ITEM, sMsg );
<SPAN class=3Dcpp-keyword>return<
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -