?? wtl for mfc programmers, part ii - wtl gui base classes - wtl.htm
字號(hào):
<html>
<head>
<title>WTL for MFC Programmers, Part II - WTL GUI Base Classes - WTL</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body bgcolor="#33CCCC" text="#000000">
<p align="center"><b><font style="FONT-SIZE: 16pt" size="4" color="#0033CC">WTL
for MFC Programmers, Part II - WTL GUI Base Classes</font></b><br>
<br>
</p>
<p align="left">原作 :<b><font color="#CC3366">Michael Dunn</font></b> [<a href="http://www.codeproject.com/wtl/WTL4MFC2.asp">英文原文</a>]<br>
翻譯 :<a href="mailto:inte2000@163.com">Orbit(桔皮干了)</a> [<a href="http://www.winmsg.com/cn/orbit.htm">http://www.winmsg.com/cn/orbit.htm</a>]</p>
<p align="left"><a href="demo/WTL4MFC2_demo.zip">下載演示程序代碼</a></p>
<H2><font color="#FFFF66">本章內(nèi)容</font></H2>
<UL>
<LI><A
href="#intopart2">對(duì)第二部分的介紹</A>
<LI><A
href="#wtloverview">WTL 的總體印象</A>
<LI><A
href="#beginningexe">開(kāi)始寫WTL程序</A>
<LI><A href="#wtlmsgmap">WTL 對(duì)消息映射鏈的增強(qiáng)</A>
<LI><A href="#appwizard">從WTL的應(yīng)用程序生成向?qū)艿玫绞裁?lt;/A>
<UL>
<LI><A
href="#thruwizard">使用向?qū)У恼麄€(gè)過(guò)程</A>
<LI><A
href="#examinecode">查看生成的代碼</A> </LI>
</UL>
<LI><A
href="#msgloopinternals">CMessageLoop 的內(nèi)部實(shí)現(xiàn)</A>
<LI><A
href="#frameinternals">CFrameWindowImpl 的內(nèi)部實(shí)現(xiàn)</A>
<LI><A
href="#backtotheclock">回到前面的時(shí)鐘程序</A>
<LI><A href="#uiupdating">UI狀態(tài)的自動(dòng)更新</A>
<UL>
<LI><A
href="#newmenuitems">添加控制時(shí)鐘的菜單</A>
<LI><A
href="#callinguienable">使用UIEnable()函數(shù)</A> </LI>
</UL>
<LI><A
href="#fixclassview"> 消息映射鏈(Message Maps)中最后需要注意的地方</A>
<LI><A href="#nextup">下一站,1995</A>
<LI><A
href="#revisionhistory">修改記錄</A> </LI>
</UL>
<P>
<H2><A name=intopart2></A><font color="#FFFF66">對(duì)第二部分的介紹</font></H2>
<P>好了,現(xiàn)在正式開(kāi)始介紹WTL!在這一部分我講的內(nèi)容包括生成一個(gè)基本的主窗口和WTL提供的一些友好的改進(jìn),比如UI界面的更新(如菜單上的選擇標(biāo)記)和更好的消息映射機(jī)制。為了更好地掌握本章的內(nèi)容,你應(yīng)該安裝WTL并將WTL庫(kù)的頭文件目錄添加到VC的搜索目錄中,還要將WTL的應(yīng)用程序生成向?qū)?fù)制到正確的位置。WTL的發(fā)布版本中有文檔具體介紹如何做這些設(shè)置,如果遇到困難可以查看這些文檔。</P>
<H2><A name=wtloverview></A><font color="#FFFF66">WTL 總體印象</font></H2>
<P>WTL的類大致可以分為幾種類型:</P>
<OL>
<LI>主框架窗口的實(shí)現(xiàn)<CODE>-</CODE> CFrameWindowImpl, CMDIFrameWindowImpl
<LI>控件的封裝- CButton, CListViewCtrl
<LI>GDI 對(duì)象的封裝- CDC, CMenu
<LI>一些特殊的界面特性 - CSplitterWindow, CUpdateUI, CDialogResize, CCustomDraw
<LI>實(shí)用的工具類和宏- CString, CRect, BEGIN_MSG_MAP_EX</LI>
</OL>
<P>本篇文章將深入地介紹框架窗口類,還將簡(jiǎn)要地講一下有關(guān)的界面特性類和工具類,這些界面特性類和工具類中絕大多數(shù)都是獨(dú)立的類,盡管有一些是嵌入類,例如:CDialogResize。</P>
<H2><A name=beginningexe></A><font color="#FFFF66">開(kāi)始寫WTL程序</font></H2>
<P>如果你沒(méi)有用WTL的應(yīng)用程序生成向?qū)б矝](méi)關(guān)系(我將在后面介紹這個(gè)向?qū)У挠梅?, WTL的程序的代碼結(jié)構(gòu)很像ATL的程序,本章使用的例子代碼有別于第一章的例子,主要是為了顯示W(wǎng)TL的特性,沒(méi)有什么實(shí)用價(jià)值。</P>
<P>這一節(jié)我們將在WTL生成的代碼基礎(chǔ)上添加代碼,生成一個(gè)新的程序,程序主窗口的客戶區(qū)顯示當(dāng)前的時(shí)間。stdafx.h的代碼如下:</P>
<PRE><SPAN class=cpp-preprocessor><font color="#0000FF">#define STRICT</font></SPAN>
<font color="#0000FF"><SPAN class=cpp-preprocessor>#define WIN32_LEAN_AND_MEAN</SPAN>
<SPAN class=cpp-preprocessor>#define _WTL_USE_CSTRING</SPAN>
<SPAN class=cpp-preprocessor>#include <atlbase.h> // 基本的ATL類</SPAN>
<SPAN class=cpp-preprocessor>#include <atlapp.h> // 基本的WTL類
</SPAN><SPAN class=cpp-keyword>extern</SPAN> CAppModule _Module; <SPAN class=cpp-comment>// WTL 派生的CComModule版本</SPAN>
<SPAN class=cpp-preprocessor>#include <atlwin.h> // ATL 窗口類</SPAN>
<SPAN class=cpp-preprocessor>#include <atlframe.h> // WTL 主框架窗口類</SPAN>
<SPAN class=cpp-preprocessor>#include <atlmisc.h> // WTL 實(shí)用工具類,例如:CString</SPAN>
<SPAN class=cpp-preprocessor>#include <atlcrack.h> // WTL 增強(qiáng)的消息宏</SPAN></font></PRE>
<P>atlapp.h 是你的工程中第一個(gè)包含的頭文件,這個(gè)文件內(nèi)定義了有關(guān)消息處理的類和CAppModule,CAppModule是從CComModule派生的類。如果你打算使用CString類,你需要手工定義_WTL_USE_CSTRING標(biāo)號(hào),因?yàn)镃String類是在atlmisc.h中定義的,而許多包含在atlmisc.h之前的頭文件都會(huì)用到CString,定義_WTL_USE_CSTRING之后,atlapp.h就會(huì)向前聲明CString類,其他的頭文件就知道CString類的存在,從而避免編譯器為此大驚小怪。<br>
</P>
<P>接下來(lái)定義框架窗口。我們的SDI窗口是從CFrameWindowImpl派生的,在定義窗口類時(shí)使用DECLARE_FRAME_WND_CLASS代替前面使用的DECLARE_WND_CLASS。下面時(shí)MyWindow.h中窗口定義的開(kāi)始部分:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CMyWindow : <SPAN class=cpp-keyword>public</SPAN> CFrameWindowImpl<CMyWindow>
{
<SPAN class=cpp-keyword>public</SPAN>:
DECLARE_FRAME_WND_CLASS(_T(<SPAN class=cpp-string>"First WTL window"</SPAN>), IDR_MAINFRAME);
BEGIN_MSG_MAP(CMyWindow)
CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
END_MSG_MAP()
};</font></PRE>
<P>DECLARE_FRAME_WND_CLASS有兩個(gè)參數(shù),窗口類名(類名可以是NULL,ATL會(huì)替你生成一個(gè)類名)和資源ID,創(chuàng)建窗口時(shí)WTL用這個(gè)ID裝載圖標(biāo),菜單和加速鍵表。我們還要象CFrameWindowImpl中的消息處理(例如WM_SIZE和WM_DESTROY消息)那樣將消息鏈入窗口的消息中。</P>
<P>現(xiàn)在來(lái)看看WinMain()函數(shù),它和第一部分中的例子代碼中的WinMain()函數(shù)幾乎一樣,只是創(chuàng)建窗口部分的代碼略微不同。</P>
<PRE>// main.cpp:<br><font color="#0000FF">#include "stdafx.h"<br>#include "MyWindow.h"<br> <br>CAppModule _Module;<br> <br>int APIENTRY WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,<br> LPSTR lpCmdLine, int nCmdShow )<br>{<br> _Module.Init ( NULL, hInstance );<br> <br>CMyWindow wndMain;<br>MSG msg;<br> <br> // Create the main window<br> if ( NULL == wndMain.CreateEx() )<br> return 1; // Window creation failed<br> <br> // Show the window<br> wndMain.ShowWindow ( nCmdShow );<br> wndMain.UpdateWindow();<br> <br> // Standard Win32 message loop<br> while ( GetMessage ( &msg, NULL, 0, 0 ) > 0 )<br> {<br> TranslateMessage ( &msg );<br> DispatchMessage ( &msg );<br> }<br> <br> _Module.Term();<br> return msg.wParam;<br>}</font></PRE>
<P>CFrameWindowImpl中的CreateEx()函數(shù)的參數(shù)使用了常用的默認(rèn)值,所以我們不需要特別指定任何參數(shù)。正如前面介紹的,CFrameWindowImpl會(huì)處理資源的裝載,你只需要使用IDR_MAINFRAME作為ID定義你的資源就行了(譯者注:主要是圖標(biāo),菜單和加速鍵表),你也可以直接使用本章的例子代碼。</P>
<P>如果你現(xiàn)在就運(yùn)行程序,你會(huì)看到主框架窗口,事實(shí)上它沒(méi)有做任何事情。我們需要手工添加一些消息處理,所以現(xiàn)在是介紹WTL的消息映射宏的最佳時(shí)間。</P>
<H2><A name=wtlmsgmap></A><font color="#FFFF66">WTL 對(duì)消息映射的增強(qiáng)</font></H2>
<P>將Win32 API通過(guò)消息傳遞過(guò)來(lái)的WPARAM和LPARAM數(shù)據(jù)還原出來(lái)是一件麻煩的事情并且很容易出錯(cuò),不幸得是ATL并沒(méi)有為我們提供更多的幫助,我們?nèi)匀恍枰獜南⒅羞€原這些數(shù)據(jù),當(dāng)然WM_COMMAND和WM_NOTIFY消息除外。但是WTL的出現(xiàn)拯救了這一切!</P>
<P>WTL的增強(qiáng)消息映射宏定義在atlcrack.h中。(這個(gè)名字來(lái)源于“消息解密者”,是一個(gè)與windowsx.h的宏所使用的相同術(shù)語(yǔ))首先將BEGIN_MSG_MAP改為BEGIN_MSG_MAP_EX,帶_EX的版本產(chǎn)生“解密”消息的代碼。</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CMyWindow : <SPAN class=cpp-keyword>public</SPAN> CFrameWindowImpl<CMyWindow>
{
<SPAN class=cpp-keyword>public</SPAN>:</font>
<FONT color=red>BEGIN_MSG_MAP_EX(CMyWindow)</FONT>
<font color="#0000FF"> CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
END_MSG_MAP()
};</font></PRE>
<P>對(duì)于我們的時(shí)鐘程序,我們需要處理WM_CREATE消息來(lái)設(shè)置定時(shí)器,WTL的消息處理使用MSG_作為前綴,后面是消息名稱,例如MSG_WM_CREATE。這些宏只是代表消息響應(yīng)處理的名稱,現(xiàn)在我們來(lái)添加對(duì)WM_CREATE消息的響應(yīng):</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CMyWindow : <SPAN class=cpp-keyword>public</SPAN> CFrameWindowImpl<CMyWindow>
{
<SPAN class=cpp-keyword>public</SPAN>:
BEGIN_MSG_MAP_EX(CMyWindow)</font><FONT color=red>
MSG_WM_CREATE(OnCreate)</FONT>
<font color="#0000FF"> CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
END_MSG_MAP()
<SPAN class=cpp-comment>// OnCreate(...) ?</SPAN>
};</font></PRE>
<P>WTL的消息響應(yīng)處理看起來(lái)有點(diǎn)象MFC,每一個(gè)處理函數(shù)根據(jù)消息傳遞的參數(shù)不同也有不同的原型。由于我們沒(méi)有向?qū)ё詣?dòng)添加消息響應(yīng),所以我們需要自己查找正確的消息處理函數(shù)。幸運(yùn)的是VC可以幫我們的忙,將鼠標(biāo)光標(biāo)移到“MSG_WM_CREATE”宏的文字上按F12鍵就可以來(lái)到這個(gè)宏的定義代碼處。如果是第一次使用這個(gè)功能,VC會(huì)要求從新編譯全部文件以建立瀏覽信息數(shù)據(jù)庫(kù)(browse
info database),建立了這個(gè)數(shù)據(jù)庫(kù)之后,VC會(huì)打開(kāi)atlcrack.h并將代碼定位到MSG_WM_CREATE的定義位置:</P>
<PRE><SPAN class=cpp-preprocessor><font color="#0000FF">#define MSG_WM_CREATE(func) \</font></SPAN>
<font color="#0000FF"><SPAN class=cpp-keyword>if</SPAN> (uMsg == WM_CREATE) \
{ \
SetMsgHandled(TRUE); \</font>
<FONT color=red>lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \</FONT>
<font color="#0000FF"><SPAN class=cpp-keyword>if</SPAN>(IsMsgHandled()) \
<SPAN class=cpp-keyword>return</SPAN> TRUE; \
}</font></PRE>
<P>標(biāo)記為紅色的那一行非常重要,就是在這里調(diào)用實(shí)際的消息響應(yīng)函數(shù),他告訴我們消息響應(yīng)函數(shù)有一個(gè)<code>LPCREATESTRUCT</code>類型的參數(shù),返回值的類型是LRESULT。請(qǐng)注意這里沒(méi)有ATL的宏所用的
bHandled 參數(shù),<code>SetMsgHandled()</code>函數(shù)代替了這個(gè)參數(shù),我會(huì)對(duì)此作些簡(jiǎn)要的介紹。</P>
<P>現(xiàn)在為我們的窗口類添加OnCreate()響應(yīng)函數(shù):</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CMyWindow : <SPAN class=cpp-keyword>public</SPAN> CFrameWindowImpl<CMyWindow>
{
<SPAN class=cpp-keyword>public</SPAN>:
BEGIN_MSG_MAP_EX(CMyWindow)
MSG_WM_CREATE(OnCreate)
CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
END_MSG_MAP()</font>
<FONT color=red>LRESULT OnCreate(LPCREATESTRUCT lpcs)
{
SetTimer ( <SPAN class=cpp-literal>1</SPAN>, <SPAN class=cpp-literal>1000</SPAN> );
SetMsgHandled(<SPAN class=cpp-keyword>false</SPAN>);
<SPAN class=cpp-keyword>return</SPAN> <SPAN class=cpp-literal>0</SPAN>;
}</FONT>
<font color="#0000FF">};</font></PRE>
<P>CFrameWindowImpl 是直接從CWindow類派生的, 所以它繼承了所有CWindow類的方法,如SetTimer()。這使得對(duì)窗口API的調(diào)用有點(diǎn)象MFC的代碼,只是MFC使用CWnd類包裝這些API。</P>
<P>我們使用SetTimer()函數(shù)創(chuàng)建一個(gè)定時(shí)器,它每隔一秒鐘(1000毫秒)觸發(fā)一次。由于我們需要讓CFrameWindowImpl也處理WM_CREATE消息,所以我們調(diào)用SetMsgHandled(false),讓消息通過(guò)CHAIN_MSG_MAP宏鏈入基類,這個(gè)調(diào)用代替了ATL宏使用的bHandled參數(shù)。(即使CFrameWindowImpl類不需要處理WM_CREATE消息,調(diào)用SetMsgHandled(false)讓消息流入基類是個(gè)好的習(xí)慣,因?yàn)檫@樣我們就不必總是記著哪個(gè)消息需要基類處理那些消息不需要基類處理,這和VC的類向?qū)Мa(chǎn)生的代碼相似,多數(shù)的派生類的消息處理函數(shù)的開(kāi)始或結(jié)尾都會(huì)調(diào)用基類的消息處理函數(shù))</P>
<P>為了能夠停止定時(shí)器我們還需要響應(yīng)WM_DESTROY消息,添加消息響應(yīng)的過(guò)程和前面一樣,MSG_WM_DESTROY宏的定義是這樣的:</P>
<PRE><SPAN class=cpp-preprocessor><font color="#0000FF">#define MSG_WM_DESTROY(func) \</font></SPAN>
<font color="#0000FF"><SPAN class=cpp-keyword>if</SPAN> (uMsg == WM_DESTROY) \
{ \
SetMsgHandled(TRUE); \</font>
<FONT color=red>func(); \</FONT>
<font color="#0000CC"> lResult = <SPAN class=cpp-literal>0</SPAN>; \
<SPAN class=cpp-keyword>if</SPAN>(IsMsgHandled()) \
<SPAN class=cpp-keyword>return</SPAN> TRUE; \
}</font></PRE>
<P>OnDestroy()函數(shù)沒(méi)有參數(shù)也沒(méi)有返回值,CFrameWindowImpl也要處理WM_DESTROY消息,所以還要調(diào)用SetMsgHandled(false):<br>
</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> CMyWindow : <SPAN class=cpp-keyword>public</SPAN> CFrameWindowImpl<CMyWindow>
{
<SPAN class=cpp-keyword>public</SPAN>:
BEGIN_MSG_MAP_EX(CMyWindow)
MSG_WM_CREATE(OnCreate)</font>
<FONT color=red>MSG_WM_DESTROY(OnDestroy)</FONT>
<font color="#0000CC"> CHAIN_MSG_MAP(CFrameWindowImpl<CMyWindow>)
END_MSG_MAP()<BR><BR></font><BR>
<FONT color=red><SPAN class=cpp-keyword>void</SPAN> OnDestroy()
{
KillTimer(<SPAN class=cpp-literal>1</SPAN>);
SetMsgHandled(<SPAN class=cpp-keyword>false</SPAN>);
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -