?? wtl for mfc programmers, part v - advanced dialog ui classes - wtl.htm
字號:
<SPAN class=cpp-keyword>bool</SPAN> SetHyperLink(LPCTSTR lpstrLink)</font></PRE>
<P>獲得或設置控件關聯(lián)超鏈接的URL,如果不指定超鏈接URL,控件會使用顯示的文字字符串作為URL。</P>
<H5><font color="#990000">Navigation</font></H5>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">bool</font></SPAN><font color="#0033FF"> Navigate()</font></PRE>
<P>導航到當前超鏈接的URL,該URL或者是由SetHyperLink()函數指定的URL,或者就是控件的窗口文字。</P>
<H5><font color="#990000">Tooltip management</font></H5>
<P>沒有公開的方法設置工具提示,所以需要直接使用CToolTipCtrl成員m_tip。</P>
<p>下圖顯示的就是ControlMania2對話框中的超鏈接控件:</p>
<P><IMG height=324 alt=" [WTL hyperlink - 12K] "
src="images/hyperlink5.png"
width=305 align=bottom border=0></P>
<P>在OnInitDialog()函數中設置URL:</P>
<PRE> <font color="#0033FF"> m_wndLink.SetHyperLink ( _T(<SPAN class=cpp-string>"http://www.codeproject.com/"</SPAN>) );</font></PRE>
<H2><A name=uiupdctrl></A><font color="#FFFF66">對話框中控件的UI Updating</font></H2>
<P>對話框中的的UI updating控制比MFC中簡單得多,在MFC中,你需要響應未公開的WM_KICKIDLE消息,處理這個消息并觸發(fā)控件的updating,在WTL中,沒有這個詭計,不過向導存在一個BUG,需要手工添加一行代碼解決這個問題。</P>
<p>首先需要記住的是對話框必須是無模式的,因為CUpdateUI需要在程序的消息循環(huán)控制下工作。如果對話框是模式的,系統(tǒng)處理消息循環(huán),我們程序的空閑處理函數就不會被調用,由于CUpdateUI是在空閑時間工作的,所以沒有空閑處理就沒有UI
updating。</p>
<p>ControlMania2的對話框是非模式的,類定義的開始部分很像是一個框架窗口類:</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-keyword>public</SPAN> CUpdateUI<CMainDlg>,
<SPAN class=cpp-keyword>public</SPAN> CMessageFilter, <SPAN class=cpp-keyword>public</SPAN> CIdleHandler
{
<SPAN class=cpp-keyword>public</SPAN>:
<SPAN class=cpp-keyword>enum</SPAN> { IDD = IDD_MAINDLG };
<SPAN class=cpp-keyword>virtual</SPAN> BOOL PreTranslateMessage(MSG* pMsg);
<SPAN class=cpp-keyword>virtual</SPAN> BOOL OnIdle();
BEGIN_MSG_MAP_EX(CMainDlg)
MSG_WM_INITDIALOG(OnInitDialog)
COMMAND_ID_HANDLER_EX(IDOK, OnOK)
COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel)
COMMAND_ID_HANDLER_EX(IDC_ALYSON_BTN, OnAlysonODBtn)
END_MSG_MAP()
BEGIN_UPDATE_UI_MAP(CMainDlg)
END_UPDATE_UI_MAP()
<SPAN class=cpp-comment>//...</SPAN>
};</font></PRE>
<P>注意CMainDlg類從CUpdateUI派生并含有一個update UI鏈。OnInitDialog()做了這些工作,這和前面介紹的框架窗口中的代碼很相似:</P>
<PRE> <font color="#0033FF"><SPAN class=cpp-comment>// register object for message filtering and idle updates</SPAN>
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(<SPAN class=cpp-keyword>this</SPAN>);
pLoop->AddIdleHandler(<SPAN class=cpp-keyword>this</SPAN>);
UIAddChildWindowContainer(m_hWnd);</font></PRE>
<P>只是這次我們不是調用UIAddToolbar()或UIAddStatusBar(),而是調用UIAddChildWindowContainer(),它告訴CUpdateUI我們的對話框含有需要updating的字窗口,只要看看OnIdle(),你會懷疑少了寫什么:</P>
<PRE><font color="#0033FF">BOOL CMainDlg::OnIdle()
{
<SPAN class=cpp-keyword>return</SPAN> FALSE;
}</font></PRE>
<P>你可能猜想這里應該調用另一個CUpdateUI的方法做一些實在的updating工作,你是對的,應該是這樣的,向導在OnIdle()中漏掉了一行代碼,現在加上:</P>
<PRE><font color="#0033FF">BOOL CMainDlg::OnIdle()
{
<B>UIUpdateChildWindows();</B>
<SPAN class=cpp-keyword>return</SPAN> FALSE;
}</font></PRE>
<P>為了演示UI updating,我們設定鼠標點擊左邊的位圖按鈕,使得右邊的按鈕變得可用或禁用。先在update UI鏈中添加一個消息入口,使用UPDUI_CHILDWINDOW標志表示此入口是子窗口類型:</P>
<PRE><font color="#0033FF"> BEGIN_UPDATE_UI_MAP(CMainDlg)
<B>UPDATE_ELEMENT(IDC_ALYSON_BMPBTN, UPDUI_CHILDWINDOW)</B>
END_UPDATE_UI_MAP()</font></PRE>
<P>在左邊的按鈕的單擊事件處理中,我們調用UIEnable()來翻轉另一個按鈕的使能狀態(tài):</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> CMainDlg::OnAlysonODBtn ( UINT uCode, <SPAN class=cpp-keyword>int</SPAN> nID, HWND hwndCtrl )
{
<SPAN class=cpp-keyword>static</SPAN> <SPAN class=cpp-keyword>bool</SPAN> s_bBtnEnabled = <SPAN class=cpp-keyword>true</SPAN>;
s_bBtnEnabled = !s_bBtnEnabled;
UIEnable ( IDC_ALYSON_BMPBTN, s_bBtnEnabled );
}</font></PRE>
<H2><A name=ddv></A><font color="#FFFF66">DDV</font></H2>
<P>WTL的對話框數據驗證(DDV)比MFC簡單一些,在MFC中你需要分別使用DDX(對話框數據交換)宏和DDV(對話框數據驗證)宏,在WTL中只需一個宏就可以了,WTL包含基本的數據驗證支持,在DDV鏈中可以使用三個宏:</P>
<DL>
<DT><CODE>DDX_TEXT_LEN</CODE>
<DD>和DDX_TEXT一樣,只是還要驗證字符串的長度(不包含結尾的空字符)小于或等于限制長度。
<DT><CODE>DDX_INT_RANGE</CODE> and <CODE>DDX_UINT_RANGE</CODE>
<DD>和DDX_INT,DDX_UINT一樣,還加了對數字的最大最小值的驗證。
<DT><CODE>DDX_FLOAT_RANGE</CODE>
<DD>除了像DDX_FLOAT一樣完成數據交換之外,還驗證數字的最大最小值。</DD>
</DL>
<P>ControlMania2有一個ID是IDC_FAV_SEASON的edit box,它和成員變量m_nSeason相關聯(lián)。</P>
<P><IMG height=429 alt=" [Season selector edit box - 13K] "
src="images/cm2_seasonedit5.png"
width=373 align=bottom border=0></P>
<P>由于有效的值是1到7,所以使用這樣的數據驗證宏:</P>
<PRE><font color="#0033FF"> BEGIN_DDX_MAP(CMainDlg)
<SPAN class=cpp-comment>//...</SPAN>
DDX_INT_RANGE(IDC_FAV_SEASON, m_nSeason, <SPAN class=cpp-literal>1</SPAN>, <SPAN class=cpp-literal>7</SPAN>)
END_DDX_MAP()</font></PRE>
<P>OnOK()調用DoDataExchange()獲得season的數值,并驗證是在1到7之間。</P>
<H3><A name=ddvfail></A><font color="#FFFF66">處理DDV驗證失敗</font></H3>
<P>如果控件的數據驗證失敗,CWinDataExchange會調用重載函數OnDataValidateError(),默認到處理是驅動PC喇叭發(fā)出聲音,你可能想給出更友好的錯誤指示。OnDataValidateError()的函數原型是:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data );</font></PRE>
<P>_XData是一個WTL的內部數據結構,CWinDataExchange根據輸入的數據和允許的數據范圍填充這個數據結構。下面是這個數據結構的定義:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">struct</font></SPAN><font color="#0033FF"> _XData
{
_XDataType nDataType;
<SPAN class=cpp-keyword>union</SPAN>
{
_XTextData textData;
_XIntData intData;
_XFloatData floatData;
};
};</font></PRE>
<P>nDataType指示聯(lián)合中的三個成員那個是有意義的,nDataType 的取值可以是:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">enum</font></SPAN><font color="#0033FF"> _XDataType
{
ddxDataNull = <SPAN class=cpp-literal>0</SPAN>,
ddxDataText = <SPAN class=cpp-literal>1</SPAN>,
ddxDataInt = <SPAN class=cpp-literal>2</SPAN>,
ddxDataFloat = <SPAN class=cpp-literal>3</SPAN>,
ddxDataDouble = <SPAN class=cpp-literal>4</SPAN>
};</font></PRE>
<P>在我們的例子中,nDataType的值是ddxDataInt,這表示_XData中的_XIntData成員是有效的,_XIntData是個簡單的數據結構:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">struct</font></SPAN><font color="#0033FF"> _XIntData
{
<SPAN class=cpp-keyword>long</SPAN> nVal;
<SPAN class=cpp-keyword>long</SPAN> nMin;
<SPAN class=cpp-keyword>long</SPAN> nMax;
};</font></PRE>
<P>我們重載OnDataValidateError()函數,顯示錯誤信息并告訴用戶允許的數值范圍:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> CMainDlg::OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data )
{
CString sMsg;
sMsg.Format ( _T(<SPAN class=cpp-string>"Enter a number between %d and %d"</SPAN>),
data.intData.nMin, data.intData.nMax );
MessageBox ( sMsg, _T(<SPAN class=cpp-string>"ControlMania2"</SPAN>), MB_ICONEXCLAMATION );
::SetFocus ( GetDlgItem(nCtrlID) );
}</font></PRE>
<P>_XData中的另外兩個結構_XTextData和_XFloatData的定義在atlddx.h中,感興趣的話可以打開這個文件查看一下。</P>
<h2><A name=resizing></A><font color="#FFFF66">改變對話框的大小</font></h2>
<P>WTL引起我的注意的第一件事是對可調整大小對話框的內建的支持。在這之前我曾寫過一篇<a href="http://www.codeproject.com/wtl/wtldlgresize.asp">關于這個主題的文章</a>,詳情請參考這篇文章。簡單的說就是將CDialogResize類添加到對話框的集成列表,在OnInitDialog()中調用DlgResize_Init(),然后將消息鏈入CDialogResize。</P>
<H2><A name=upnext></A><font color="#FFFF66">繼續(xù)</font></H2>
<P>下一章,我將介紹如何在對話框中使用ActiveX控件和如何處理控件觸發(fā)的事件。</P>
<H2><A name=references></A><font color="#FFFF66">參考</font></H2>
<P><A href="http://www.codeproject.com/wtl/wtldlgresize.asp">Using WTL's
Built-in Dialog Resizing Class</A> - Michael Dunn</P>
<P><A href="http://www.codeproject.com/wtl/propsheetddx.asp">Using DDX and
DDV with WTL</A> - Less Wright</P>
<H2><A name=revisionhistory></A><font color="#FFFF66">修改記錄</font></H2>
<P>2003年4月28日,本文第一次發(fā)表。</P>
</body>
</html>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -