WTL是一個好東東.它開發的程序都非常短小精悍.對開發WIN32的應用有非常好的好處.它不用MFC開發.但能夠快速產生窗口和控件.
以文本方式查看主題
- 溫馨小築 (http://www.learnsky.com/bbs/index.asp)
-- 電腦編程 (http://www.learnsky.com/bbs/list.asp?boardid=6)
---- WTL簡介 (http://www.learnsky.com/bbs/dispbbs.asp?boardid=6&id=407)
-- 作者:admin
-- 發佈時間:2005-1-11 2:08:00
-- WTL簡介
WTL簡介
vcmfc
在ATL出現的時候,一些部分COM的編程人員開始覺得開發COM運用是一種快樂,因爲使用它很方便地開發小規模的COM組件,但好景不長,現實的COM組件是包羅相當廣泛的,特別當它們準備使用包裝我窗口控件,發現ATL提供了相當的稀少。因此Microsoft推出了半成品與沒有技術支持的WTL,這也是WTL誕生的原因。
很多初次接觸WTL都問“WTL這三個字母代表什麼呢?”:WTL全稱爲Windows Template Library,構架於ATL之上,採用C++模板技術來包裝大部窗口控制,並給出一個與MFC相似的應用框架。
他們緊跟着問“那我如何得到它呢?”:由於WTL是Microsoft推出的,在Microsoft的PlatForm SDK中就有,以下是部分畫面:
或者能過以下鏈接下載:http://msdn.microsoft.com/msdn-files/027/001/586/wtl31.exe
跟着問題又來了,“我該如何使用它們呢?”:在你安裝完了WTL SDK之後,在安裝目錄中有一個AtlApp60.Awx的嚮導文件,將它拷貝到你安裝Visual C++的目錄:Microsoft Visual Studio//common//mesdev98//bin//ide//目錄下(實在不行使用Windows的搜索文件查找.awx),這是,在VC的應用程序嚮導裏就有跟MFC相似的WTL應用程序嚮導。
如果你是MFC的使用者,你可能會再問“WTL與MFC在包裝窗口控制有哪些不同呢?”:我只能用以下表格回答你:
Feature |
MFC |
WTL |
Stand-alone library |
Yes |
No (built on ATL) |
AppWizard support |
Yes |
Yes |
Clazard support |
Yes |
No |
Officially supported by Microsoft |
Yes |
No (Supported by volunteers inside MS) |
Support for OLE Documents |
Yes |
No |
Support for Views |
Yes |
Yes |
Support for Documents |
Yes |
No |
Basic Win32 & Common Control Wrappers |
Yes |
Yes |
Advanced Common Control Wrappers (Flat scrollbar, IP Address, Pager Control, etc.) |
No |
Yes |
Command Bar support (including bitmapped context menus) |
No (MFC does provide dialog bars) |
Yes |
CString |
Yes |
Yes |
GDI wrappers |
Yes |
Yes |
Helper classes (CRect, Cpoint, etc.) |
Yes |
Yes |
Property Sheets/Wizards |
Yes |
Yes |
SDI, MDI support |
Yes |
Yes |
Multi-SDI support |
No |
Yes |
MRU Support |
Yes |
Yes |
Docking Windows/Bars |
Yes |
No |
Splitters |
Yes |
Yes |
DDX |
Yes |
Yes (not as extensive as MFC) |
Printing/Print Preview |
Yes |
Yes |
Scrollable Views |
Yes |
Yes |
Custom Draw/Owner Draw Wrapper |
No |
Yes |
Message/Command Routing |
Yes |
Yes |
Common Dialogs |
Yes |
Yes |
HTML Views |
Yes |
Yes |
Single Instance Applications |
No |
No |
UI Updating |
Yes |
Yes |
Template-based |
No |
Yes |
Size of a statically linked do-nothing SDI application with toolbar, status bar, and menu |
228KB + |
24k (with /OPT:NOWIN98) |
Size of a dynamically linked do-nothing SDI application with toolbar, status bar, and menu |
24KB + |
N/A |
Runtime Dependencies |
CRT (+ MFC42.DLL, if dynamically linked) |
None (CRT if you use CString) |
最後再說兩句。由於WTL不是Microsoft的正式產品,因此得不到Microsoft的技術支持,雖然有不少民間技術團體的支持,但這還不夠;關於WTL的技術文章相當的少,而且WTL使用C++的Template技術,這是一種相對較新的技術,無法與MFC混合使用,使用它需要重新學習它,以致於相當少的人使用它。
-- 作者:admin
-- 發佈時間:2005-1-11 2:11:00
--
什麼是WTL? 選擇自 dairyman000 的 Blog
關鍵字 WTL ATL COM
出處 http://www.idevresource.com/com/library/bytesize/wtl.asp
簡介
WTL 在開發者之間的悄悄傳播已經超過一年了, 傳聞它是基於ATL的,並在微軟內部使用.這理所當然的引起了ATL開發者社區的主意.這些人從ATL1.1開始,就一直爲ATL控件書寫UI代碼,但是他們發現,他們的所寫的代碼常常就是純的Win32 GDI代碼.我告訴您, WTL並沒有多大不同.
是不是讓人失望? 不,因爲ATL只是對COM進行了簡單的封裝,這也是ATL的強大之處. 是的,寫ATL您必須通曉COM. 您在ATL上額外花費的功夫跟您學習COM所作的努力比起來,簡直微不足道.這跟那些需要把主要精力花費在學習類庫本身,忽略COM的庫是完全不同的.
WTL與此類似.您需要懂得Win32窗口技術和GDI.只要您懂得,學習WTL就似清風撫面,再簡單不過了.如果您不懂 這些,那麼您最好使用VB來寫UI代碼.
WTL有什麼?
它給各種類型的應用程序提供了一個基本的框架.注意,雖然您沒有MFC那樣的文檔/視結構,但是您有視(views). 在WTL有大量的代碼讓您來管理視,而且加入您自己的代碼也很容易. WTL有AppWizard,可以讓您生成SDI, MDI 和多線程SDI程序多線程SDI跟IE或Windows Explorer很像.它看起來是打開了多個程序實例,實際上這些窗口都是屬於一個進程的).
另外,您的程序可以是基於對話框的,也可以是基於視的.視可以是基於CWindowImpl的,也可以是基於控件,甚至是IE裏的一個HTML頁.您可以選擇您的程序是否需要一個rebar, command bar (CE-like), toolbar 和/或status bar.另外,您的程序可以主持ActiveX控件,以及成爲一個COM服務器.
這裏有幾個關於視的選項. WTL提供splitter窗口類(這樣在一個視裏您可以有兩個窗口)和scroll窗口類(這樣您的窗口可以比它顯示的"視"小). WTL也有個類似MFC的UpDateUI的東西,但是它們不是很一樣 - 主要的區別是您需要把需要更新的項用宏映射標註出來,然後您在您的類里加入執行UpdateUI的代碼. DDX/DDV在WTL也支持,同樣類似MFC,但有不同. 您必須加一個宏映射來實現DoDataExchange,然後加入調用它的代碼.
現在WTL也有GDI類了.然而,HDC的封裝類就像CWindow一樣,只進行了很簡單的封裝 - 它幾乎沒有加入任何新的功能.不過,在WTL,你可以得到播放meta文件和OpenGL支持. 最有價值的我猜應該是打印機DC的那些繼承類 - WTL有打印機支持,甚至打印預覽. 當然也有GDI對象的封裝. 諸如畫筆,畫刷,區域等.
WTL對所有的Win32 (和W2K) 通用對話框進行了封裝.同樣儘管簡單,但是它的確使請求字體或者文件變的非常的簡單.
合成了舊的AtlControls.h,新加了一些封裝類. 這些封裝類封裝了W2K控件,以及一些不屬於Win32的"控件",像Command Bar, bitmap button, hyperlink 和 wait cursor.
WTL 最終把消息分離帶入了ATL! 一些新的MSG映射宏將消息分離,調用您類裏的消息處理函數.消息處理函數的參數的值是從消息分離得到的.唯一令人頭痛的是,您需要查看頭文件以確定函數參數的意義.
最後,WTL還有一些實用類.最重要的是CString. 不錯,它是從MFC克隆得到的(copy on write),具有(在我知道的範圍內)MFC版本的所有方法.還有查找文件的API的封裝類,以及CRect, CSize and CPoint.
總結
如果您打算寫一個Win32 界面程序,我建議您在考慮MFC之前,先試試WTL.使用WTL來寫您的代碼, 程序將變得小巧些,也更有效率些.使用WTL, 您還將得到ATL支持COM好處.而MFC沒有對COM的支持.
您可以在2000年一月份的平臺SDK中找到WTL.在MSI選項頁的Source Code section下.
作者Blog:http://blog.csdn.net/dairyman000/
-- 作者:admin
-- 發佈時間:2005-1-11 2:11:00
--
WTL體系結構
緒論
WTL最終來了,而且提供了我所希望的功能.我在WTL Bytesize(譯文)的文章列出WTL主要特徵.在本文中,我將描述一下WTL的體系結構,同時我會給出一些簡單的例子來演示如何使用它的那些特徵.希望能夠對您有所幫助.
WTL應用程序的類型
WTL有好幾種應用程序類型,供您在AppWizard選取.
下表對這些應用程序進行了描述. 這種彈性構成了WTL體系結構的一部分.
應用程序類型 | 描述 |
SDI Application | 單文本界面 – 只有一個窗口 |
Multiple Threads SDI | 單個進程擁有一個或多個窗口 |
MDI Application | 多文本界面 – 在框架內,您可以有零個或多個子窗口 |
Dialog Based | 基於對話框模版 |
你可能還是首次聽說多線程SDI應用程序,但是不用擔心,它的概念很容易理解.一個多線程SDI程序啓動後它會有一個窗口, 窗口顯示了一個文檔. 當你想要程序要再創建一個文檔時,問題就出現了--SDI程序只能顯示一個文檔.爲了解決這個問題,多線程SDI創建了另一個SDI窗口.看起來是一個新的實例在運行,實際上它不過是原來的進程創建了一個新的窗口,並把它依附到進程的一個新線程. IE的新建窗口就是這樣做的.
除了多線程SDI,所有這些應用程序都可以作爲COM服務器, 並且應用程序嚮導(AppWizard)爲此提供了一個選項.另外應用程序嚮導還可以讓你指定該程序是否主持ActiveX控件.令人費解的是,不同的程序類型,選取"Host ActiveX Controls"的地方不同.除對話框應用程序外的其他類型在第一頁上選取,而對話框類型卻放到第二頁.
第二頁的其他選項,對對話框程序以外的類型都是可用的.它們讓你指定程序是否需要工具條(toolbar),狀態條(status bar)和視窗口(View Window).
如果選取了"Toolbar"選項,你可以通過"Rebar"選擇是否將工具條放入IE Rebar控件中. 如果你選取了Rebar, 你就可以通過框架窗口(frame window)的成員m_hWndToolBar(後邊會有詳細的描述)來訪問它.你可以按照你的意願,在裏邊加入其他的工具條. 選取了"Rebar"後, 你可以決定是否選取"Command Bar". Command bar很像CE的command bar控件.只是WTL是用一個類來實現,而在CE, command bar是一個系統窗口類(system window class). Command bar非常有用,它能夠把窗口也加入到工具條中去. 如果你選取了這個選項, 工具條和菜單都將被當做toolbar來實現.這使菜單項也可以有關聯的圖標,並且當你移動鼠標到一個菜單項上時,該菜單項會被置成高亮.從Office 97以來, Office軟件的菜單都具有上述特徵.
第二頁還有指定程序是否使用視的選項(多半你想要使用), 同時你可以決定這些視如何實現. 下表列出了所有可選的視.
視 | 描述 |
Generic Window | 一個簡單的窗口. 此類窗口允許程序員編寫WM_PAINT消息的處理函數. 適用於需要直接進行paint的文檔. |
Form | 這類視具有一個對話框模版.適用於帶ActiveX 控件的窗口. 應用程序來操作這些控件. |
List Box | 這個視是個list box.它最簡單的形式意味着可以通過調用AddString() 方法來添加字符串. |
Edit | 這個視是個edit control. 本質上,它提供了一個像Notepad一樣的程序. |
List View | 這個視是個list view 通用控件.用這個控件來顯示相關的項(比如, 控制面板是一個Explorer主持的List View, 所有的項都是控制面板applet). |
Tree View | 這個視是個tree view 通用控件. 這個適用於具有層次關係的數據,比如,可以用它來顯示數據庫的schema. 頂層分支爲表和存儲過程,次級的分支爲表中的字段. |
Rich Edit | 這個視是個rich edit 控件,像WordPad. |
HTML Page | 這個視主持了一個IE Web Browser 控件. 它把主持的一個web page當成一個視. |
本文的例子需要一個對話框模版,同時還需要菜單,因此Form view是個理想的選擇.
-- 作者:admin
-- 發佈時間:2005-1-11 2:14:00
--
WTL體系結構
程序線程
跟ATL一樣,WTL程序也需要一個_Module全局變量來保存全局數據,方便應用級代碼訪問.在WTL中,這個變量是CAppModule或CServerAppModule的實例,後者在程序同時作爲一個COM服務器時用到.每個應用程序具有一個或者多個UI線程.WTL使用兩種方式來管理這些線程.
如果應用程序只有一個UI線程(除了多線程SDI以外,其他程序類型默認只有一個UI線程),線程調用全局函數run():
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CMainFrame wndMain;
if (wndMain.CreateEx() == NULL)
{
ATLTRACE(_T("Main window creation failed!//n"));
return 0;
}
wndMain.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
線程的消息循環包含在CMessageLoop內部.函數創建了一個CMessageLoop實例, 把它放入全局的消息循環映射(message loop map)數組. 以線程ID爲索引,線程中運行的其他的代碼可以訪問到這個實例. 消息循環對象包含了message filter和idle handler. 運行在這個UI線程的UI元件(UI element)可以有它自己的idle handler,在線程的消息隊列爲空時運行【譯註:通過CMessageLoop::AddIdleHandler()把這個UI元件加入到CMessageLoop的idle handler 數組中】. CMessageLoop::Run()包含了UI線程的主消息映射(main message map).下邊是它的僞代碼:
MSG m_msg;
int CMessageLoop::Run()
{
for (;;)
{
while (!::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))
DoIdleHandlers();
bRet = ::GetMessage(&m_msg, NULL, 0, 0);
if(bRet == -1)
continue;
else if(!bRet)
break;
if (!DoMessageFilters(&m_msg))
{
::TranslateMessage(&m_msg);
::DispatchMessage(&m_msg);
}
}
return (int)m_msg.wParam;
}
可以看到,這個函數推動着消息隊列. 沒有消息時, 運行註冊到線程的idle hander. 如果在隊列中檢測到消息,把它取出來,傳給每個message filter. 如果消息沒有被這些函數處理,它將按照通常的方式,發送到目標窗口.
如果程序有超過一個的UI線程,可以用WTL的線程管理器,多線程SDI就是這樣做的. 主線程作爲一個管理者線程,它會爲每個新窗口創建一個新的新線程. 主要流程如下:
int nRet = m_dwCount;
DWORD dwRet;
while(m_dwCount > 0)
{
dwRet = ::MsgWaitForMultipleObjects(m_dwCount, m_arrThreadHandles,
FALSE, INFINITE, QS_ALLINPUT);
if(dwRet >= WAIT_OBJECT_0 && dwRet <= (WAIT_OBJECT_0 + m_dwCount - 1))
RemoveThread(dwRet - WAIT_OBJECT_0);
else if(dwRet == (WAIT_OBJECT_0 + m_dwCount))
{
::GetMessage(&msg, NULL, 0, 0);
if(msg.message == WM_USER)
AddThread(_T(""), SW_SHOWNORMAL);
}
}
那些線程句柄放在一個數組中. 線程通過AddThread()加入到數組(同時啓動線程), RemoveThread()從數組移走. wait語句在兩種情況下會被打斷: 線程死亡(將線程從數組中移出) 或線程收到了WM_USER消息(一個線程在一個新線程裏新建了一個窗口). 線程管理者爲程序中的一個類,因此可以在循環中加入自己的message handler, 比如,當程序有不止一種窗口類型時. 創建一個新的窗口非常簡單,只需在任意一個窗口中調用:
::PostThreadMessage(_Module.m_dwMainThreadID, WM_USER, 0, 0L);
這個循環會一直運行下去,直到所有的UI線程都關閉了. UI線程具有一個thread procedure,它跟單UI線程的Run()方法一樣.不過,由於線程管理者使用了MsgWaitForMultipleObjects(), 這意味者最多只能有MAXIMUM_WAIT_OBJECTS-1個UI線程,這也意味着最多隻能創建63個窗口.
框架
WTL實際上是兩類窗口: 框架窗口和視圖窗口. 正如名字所暗示的那樣, 框架窗口爲窗口提供標題欄(caption bar)和邊框,你的代碼用它來處理工具條(tool bar)和菜單項命令.你看到的程序窗口實際上是視圖窗口, 視圖覆蓋了框架窗口的客戶區.客戶區是指框架窗口沒有被諸如狀態條,工具條之類的修飾部件所遮擋的部分.
線程會創建主框架窗口的一個實例,創建視圖的工作由主框架窗口的WM_CREATE消息處理函數完成. 對於SDI程序來說,這個過程很簡單. 把視圖類的一個實例作爲主框架類的一個成員,調用視圖類的Create()方法即可.MDI程序稍微有些不同, MDI主框架窗口通過CMDIFrameWindowImpl<>::CreateMDIClient()建立一個名爲MDICLIENT的窗口. 這個客戶窗口將CMDIChildWindowImpl<>窗口當做它的子窗口,子窗口有一個視圖.這也反映了這麼一個事實,MDI程序可以具有零個或者多個子窗口,每個都有邊框和標題欄.
框架窗口的OnCreate()很有意思,讓我看看:
LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
{
// create command bar window
HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault,
NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
// attach menu
m_CmdBar.AttachMenu(GetMenu());
// load command bar images
m_CmdBar.LoadImages(IDR_MAINFRAME);
// remove old menu
SetMenu(NULL);
HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME,
FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);
CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
AddSimpleReBarBand(hWndCmdBar);
AddSimpleReBarBand(hWndToolBar, NULL, TRUE);
CreateSimpleStatusBar();
m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
WS_EX_CLIENTEDGE);
UIAddToolBar(hWndToolBar);
UISetCheck(ID_VIEW_TOOLBAR, 1);
UISetCheck(ID_VIEW_STATUS_BAR, 1);
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
return 0;
}
這是從一個SDI程序拿來的一段代碼,該程序有一個基於command bar的工具條和一個狀態條. 函數的第一行創建了一個command bar實例,然後對它進行初始化,在其中加入框架窗口的菜單和工具條位圖. 這段代碼先將菜單取出,把所有的下拉菜單轉換爲工具條按鈕,並將菜單保存在一個變量中,以備後用. 給人的感覺是菜單是由工具條實現的-那我們就把它叫做工具條菜單(menu toolbar)吧. 然後Command Bar將程序工具條的圖標裝入image list 並將它們的ID保存在數組中. 當點擊工具條菜單的按鈕時,commandbar會找到對應的子菜單,創建一個彈出菜單. Command bar將子菜單項的ID和它保存的ID進行比較,這些ID跟image list中的工具條按鈕圖標是相關聯的. 如果比較成功, 則將關聯的圖標加到菜單項上去. 這意味着相同ID的菜單項和工具條按鈕具有相同的圖標.
接下來, 創建工具條並把它關聯到commandbar, 然後創建狀態條和視圖.可以看到視圖的HWND存放在框架窗口的m_hWndClient變量中. 這個窗口句柄在框架窗口的WM_SIZE handler中會用到.當框架窗口改變大小時,它告知視圖改變自身,於此同時也要考慮狀態條和command bar.
在下來的三行(從調用UIAddToolBar()開始) 用來顯示在運行時會改變狀態的UI項(UI item).文章後面還會重提這個話題. 最後,訪問消息循環(message loop), 你應該還記得該消息循環存放在一全局數組中. GetMessageLoop() 取得當前線程的消息循環,加入框架窗口的message filter和idle handler, 分別默認是PreTranslateMessage()和OnIdle().
框架窗口繼承於以下類:
class CMainFrame :
public CFrameWindowImpl<CMainFrame>,
public CUpdateUI<CMainFrame>,
public CMessageFilter,
public CIdleHandler
後兩個抽象類宣稱了框架窗口類實現了PreTranslateMessage()和OnIdle(). 從CUpdateUI<>繼承表示框架類支持UI update map.
-- 作者:admin
-- 發佈時間:2005-1-11 2:15:00
--
WTL體系結構
視圖
視圖窗口看起來顯得很簡單:
class CMyView : public CWindowImpl<CMyView>
{
public:
DECLARE_WND_CLASS(NULL)
BOOL PreTranslateMessage(MSG* pMsg)
{
pMsg;
return FALSE;
}
BEGIN_MSG_MAP(CMyView)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
END_MSG_MAP()
LRESULT OnPaint(UINT, WPARAM, LPARAM, BOOL&)
{
CPaintDC dc(m_hWnd);
//TOD Add your drawing code here
return 0;
}
};
上面是一個SDI程序的視圖類. 多線程SDI和MDI的視圖類在本質上也跟這個一樣,但他們沒有PreTranslateMessage()方法. SDI程序就是使用這個函數,趕在框架類處理消息之前把消息抓住. PreTranslateMessage()在SDI的框架類中的實現是,直接將消息轉發給視圖類.
這裏顯示的視圖實際上沒有做什麼工作.你應該自己在OnPaint()函數中加入畫出文檔內容的代碼.如果需要支持輸入,如鼠標的點擊和鍵盤的按鍵,你應該加入相應消息處理函數到類和映射中. 可以看到這個窗口是從CWindowImpl<>繼承下來的,如果你想讓它基於一個Win32控件的話,就應該從定義在AtlCtrls.h文件中某個WTL類繼承.
如果想在基於CWindowImpl<>的類里加上滾動條,那麼你應該把基類換成CScrollWindowImpl<>,同時把消息鏈給它:
class CMyView : public CScrollWindowImpl<CMyView>
{
public:
typedef CScrollWindowImpl<CMyView> parent;
BEGIN_MSG_MAP(CMyView)
CHAIN_MSG_MAP(parent)
END_MSG_MAP()
void DoPaint(CDCHandle dc)
{
}
};
基類保證窗口具備滾動條,並提供滾動條消息的默認處理.視圖類不再有WM_PAINT的處理函數,因爲它已被CScrollWindowImpl<>處理.根據滾動條的位置,CScrollWindowImpl<>畫出視圖相對應的部分. 取而代之的是,在你的類裏實現DoPaint(),在這裏你需要畫出整個視圖.如果你想指定滾動的範圍,大小或起點,你需要加上處理WM_CREATE消息的函數,把這些初始化代碼放到裏邊.
正如我先前所提到的,框架窗口會改變視圖窗口的大小,以使它客戶區未被狀態條和工具條覆蓋的部分爲視圖所填充. 在大多數情況下,這樣就夠了.但是當你想要一個具有Windows Explorer樣子的程序時,該怎麼辦呢? Windows Explorer的窗口包含了一個tree view 和一個list view,還有兩者之間的分割條. WTL的解決方案很簡單:使用splitter窗口!
爲此你需要改變一下框架窗口,讓它創建splitter窗口的一個實例作爲它的視圖. 例如, 在你的框架類裏有如下的數據成員:
CSplitterWindow m_view;
CTreeViewCtrl m_tree;
CListViewCtrl m_list;
你可以在OnCreate()創建一個splitter窗口:
// get the frame client rect, so that we set the splitter initial size
// and we can get the splitter bar in the centreRECT rect;
GetClientRect(&rect);
m_hWndClient = m_view.Create(m_hWnd, rect,
NULL, WS_CHILD | WS_VISIBLE);
m_tree.Create(m_view, rcDefault, NULL,
WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT,
WS_EX_CLIENTEDGE);
m_list.Create(m_view, rcDefault,
NULL, WS_CHILD | WS_VISIBLE | LVS_REPORT, WS_EX_CLIENTEDGE);
m_view.SetSplitterPanes(m_tree, m_list);
m_view.SetSplitterPos();
Splitter窗口如同一個視圖,將框架窗口作爲它的父窗口. 在這段代碼裏,我將框架窗口客戶區的實際大小傳給了splitter窗口. 我也可以在這裏使用 rcDefault,因爲一旦框架窗口創建完成,框架窗口就會轉發WM_SIZE消息給splitter. 這樣splitter可以馬上改變自身的大小來填充框架. 然而,當我準備使用不帶參數的SetSplitterPos(),把分割條設置於窗口中線時,出現了問題.Splitter窗口使用它的大小來決定中線的位置,由於rcDefault告訴窗口它的大小是0(因此中線的位置也是0),從而意味着分割條將出現在z最左邊,將左窗口隱藏了起來.
創建了splitter窗口後,你需要創建那些你想要分割的窗口了.它們將作爲splitter窗口的子窗口被創建.最後你將這些子窗口通過SetSplitterPanes()加到splitter窗口中去,並確定分割條的位置所在.
UI Update
菜單項可以被設置爲有效或無效,可以帶check記號或着像radio按鈕一樣,在一組菜單項中同時有且只有一個能被check.此外,菜單項還可以帶圖標和文字. 所有的這些狀態都可以在運行時根據程序中的某個值進行改變.工具條在某種程度上可以看做是菜單的易見形態,因爲它們的按鈕可以個別地,或者作爲一組的一部分被置成有效或無效,推入推出. UI update機制允許你指定哪些UI元件(UI element)的狀態可以在運行時改變. WTL使用如下的UI update映射來實現這一功能:
BEGIN_UPDATE_UI_MAP(CMainFrame)
UPDATE_ELEMENT(ID_FILE_SAVERESULTS, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
END_UPDATE_UI_MAP()
這個例子指出三個菜單項在運行時有一個狀態需要顯示,其中的一個, ID_FILE_SAVERESULTS,還有一個工具條按鈕跟它相關聯. WTL通過建立一個數組來保存這些信息.爲此你需要完成兩方面的工作:
首先是UI元件的狀態. 如果是菜單項, 你可以使用UIEnable()使能該菜單項, UISetCheck()設置check記號, UISetText()改變菜單的文字.如果是工具條按鈕,那麼你使用UIEnable()使能該按鈕, UISetCheck()或者UISetRadio()決定按鈕是推入還是推出.下邊的代碼根據是否有文本被選中,來使能Cut菜單項和工具條按鈕:
BOOL bSelected = GetSelected();
UIEnable(ID_EDIT_CUT, bSelected);
你可以把這樣的代碼放入相應處理函數中(如一個菜單項的狀態依賴於另一個菜單項的動作,將它放入後者的處理函數中),或者放入OnIdle()方法,通過檢查某個類變量來決定元件的狀態.
其次是確定各個UI元件是否都被更新了,爲此你需要調用CUpdateUI<>的某個方法將UI元件加入到列表中.主菜單已被自動加入,但是其他的任何菜單和所有的工具條必須分別通過調用UIAddMenuBar()和UIAddToolBar()手動加入.
其他還有一堆事情要注意. 首先,設置了工具條的狀態後,使用UIUpdateToolBar()以使工具條狀態更新. 對於菜單,你不需如此,因爲子菜單是動態生成的.UIUpdateMenuBar()這個方法也存在,但是它的作用是把菜單恢復到初始狀態,如果你改變過某些項的文字,調用UIUpdateMenuBar()的結果可能不是你所期望的(因爲菜單項的文字會變成老的).
儘管還有一個方法UISetRadio(),但是還沒有一個把幾個菜單項或者工具條按鈕當做radio按鈕組(也就是說,有一個而且只有一個被選中)的機制.如果你希望得到這樣效果,你必須自己編碼,不過它並不難.