WTL简单介绍
关键词: WTL
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)
|
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 + MSVCRT.DLL (288KB)
|
24k (with /OPT:NOWIN98) (+ MSVCRT.DLL if you use CString)
|
Size of a dynamically linked do-nothing SDI application with toolbar, status bar, and menu
|
24KB + MFC42.DLL (972KB) + MSVCRT.DLL (288KB)
|
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 keyword 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有大量的代码让您来管理视,并且增加您自己的代码也非常easy. WTL有AppWizard,能够让您生成SDI, MDI 和多线程SDI程序多线程SDI跟IE或Windows Explorer非常像.它看起来是打开了多个程序实例,实际上这些窗体都是属于一个进程的).
另外,您的程序能够是基于对话框的,也能够是基于视的.视能够是基于CWindowImpl的,也能够是基于控件,甚至是IE里的一个HTML页.您能够选择您的程序是否须要一个rebar, command bar (CE-like), toolbar 和/或status bar.另外,您的程序能够主持ActiveX控件,以及成为一个COMserver.
这里有几个关于视的选项. WTL提供splitter窗体类(这样在一个视里您能够有两个窗体)和scroll窗体类(这样您的窗体能够比它显示的"视"小). WTL也有个相似MFC的UpDateUI的东西,可是它们不是非常一样 - 基本的差别是您须要把须要更新的项用宏映射标注出来,然后您在您的类里增加运行UpdateUI的代码. DDX/DDV在WTL也支持,相同相似MFC,但有不同. 您必须加一个宏映射来实现DoDataExchange,然后增加调用它的代码.
如今WTL也有GDI类了.然而,HDC的封装类就像CWindow一样,仅仅进行了非常easy的封装 - 它差点儿没有增加不论什么新的功能.只是,在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应用程序,可是不用操心,它的概念非常easy理解.一个多线程SDI程序启动后它会有一个窗体, 窗体显示了一个文档. 当你想要程序要再创建一个文档时,问题就出现了--SDI程序仅仅能显示一个文档.为了解决问题,多线程SDI创建了还有一个SDI窗体.看起来是一个新的实例在执行,实际上它只是是原来的进程创建了一个新的窗体,并把它依附到进程的一个新线程. IE的新建窗体就是这样做的.
除了多线程SDI,全部这些应用程序都能够作为COMserver, 而且应用程序向导(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的实例,后者在程序同一时候作为一个COMserver时用到.每一个应用程序具有一个或者多个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, 比方,当程序有不止一种窗体类型时. 创建一个新的窗体非常easy,仅仅需在随意一个窗体中调用:
::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程序来说,这个过程非常easy. 把视图类的一个实例作为主框架类的一个成员,调用视图类的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实例,然后对它进行初始化,在当中增加框架窗体的菜单和工具条位图. 这段代码先将菜单取出,把全部的下拉菜单转换为工具条button,并将菜单保存在一个变量中,以备后用. 给人的感觉是菜单是由工具条实现的-那我们就把它叫做工具条菜单(menu toolbar)吧. 然后Command Bar将程序工具条的图标装入image list 并将它们的ID保存在数组中. 当点击工具条菜单的button时,commandbar会找到相应的子菜单,创建一个弹出菜单. Command bar将子菜单项的ID和它保存的ID进行比較,这些ID跟image list中的工具条button图标是相关联的. 假设比較成功, 则将关联的图标加到菜单项上去. 这意味着同样ID的菜单项和工具条button具有同样的图标.
接下来, 创建工具条并把它关联到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体系结构
视图
视图窗体看起来显得非常easy:
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的解决方式非常easy:使用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记号或着像radiobutton一样,在一组菜单项中同一时候有且仅仅有一个能被check.此外,菜单项还能够带图标和文字. 全部的这些状态都能够在执行时依据程序中的某个值进行改变.工具条在某种程度上能够看做是菜单的易见形态,由于它们的button能够个别地,或者作为一组的一部分被置成有效或无效,推入推出. 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,另一个工具条button跟它相关联. WTL通过建立一个数组来保存这些信息.为此你须要完毕双方面的工作:
首先是UI元件的状态. 假设是菜单项, 你能够使用UIEnable()使能该菜单项, UISetCheck()设置check记号, UISetText()改变菜单的文字.假设是工具条button,那么你使用UIEnable()使能该button, UISetCheck()或者UISetRadio()决定button是推入还是推出.下边的代码依据是否有文本被选中,来使能Cut菜单项和工具条button:
BOOL bSelected = GetSelected(); UIEnable(ID_EDIT_CUT, bSelected);
你能够把这种代码放入对应处理函数中(如一个菜单项的状态依赖于另一个菜单项的动作,将它放入后者的处理函数中),或者放入OnIdle()方法,通过检查某个类变量来决定元件的状态.
其次是确定各个UI元件是否都被更新了,为此你须要调用CUpdateUI<>的某个方法将UI元件增加到列表中.主菜单已被自己主动增加,可是其它的不论什么菜单和全部的工具条必须分别通过调用UIAddMenuBar()和UIAddToolBar()手动增加.
其它另一堆事情要注意. 首先,设置了工具条的状态后,使用UIUpdateToolBar()以使工具条状态更新. 对于菜单,你不需如此,由于子菜单是动态生成的.UIUpdateMenuBar()这种方法也存在,可是它的作用是把菜单恢复到初始状态,假设你改变过某些项的文字,调用UIUpdateMenuBar()的结果可能不是你所期望的(由于菜单项的文字会变成老的).
虽然另一个方法UISetRadio(),可是还没有一个把几个菜单项或者工具条button当做radiobutton组(也就是说,有一个并且仅仅有一个被选中)的机制.假设你希望得到这样效果,你必须自己编码,只是它并不难.
|
-- 作者:admin -- 公布时间:2005-1-11 2:16:00
--
WTL体系结构
对话框 ATL的对话框支持一向非常好,对此WTL新增了通用对话框的封装. 本质上是为对话框增加了输入验证和回调函数. 比方, 你想在用户改变年Open对话框中的目录时有所动作,那么你应该从CFileDialogImpl<>继承一个类,实现OnFolderChange():
class CMyFileDialog : public CFileDialogImpl<CMyFileDialog> { public: CMyFileDialog(BOOL b) : CFileDialogImpl<CMyFileDialog>(b) { } void OnFolderChange(LPOFNOTIFY lpon) { char strFolder[MAX_PATH]; if (GetFolderPath(strFolder, sizeof(strFolder)) > 0) { MessageBox(strFolder); } } };
当目录的路径改变时,CFileDialogImpl<>调用OnFolderChange().该函数使用基类的GetFolderPath(),来取得新路径.
控件
WTL为全部的Win32和通用控件提供了封装类,包含Windows 2000新增加的. 尽管仅仅是简单的包装,可是它们使这些控件更加easy訪问.譬如,你能记清楚从List View读出当前选定项的文字的消息和须要传的參数吗?(实际上, 你须要发送两个消息, 一个是得到选定项的索引,还有一个是读出它的文字.) WTL的作者为你完毕了这些烦人的工作, 提供了一个简单的封装函数供你使用.
使用这些控件类有两种方法. 假设你的对话框里有一个控件, 你能够将控件的HWND依附到一个封装对象,使用封装类的方法来訪问控件.这样的方法简化了你读写控件数据和处理notification消息的代码.
另外的使用方法是把这些类加到你的视图类的继承层次中去:
class CMyView : public CWindowImpl<CMyView, CListBox>
这表示CWindowImpl<>是从CListBox继承而来,因此创建的窗体将是一个list box (由于窗体类的名字是通过调用 CListBox::GetWndClassName()得到的). 另外, ATL的窗体机制会子类化这个窗体,将发给它的消息路由到你的消息映射中去. 它保留了老的窗体函数,这样,你没有处理的消息将由老的窗体函数来处理.当你的视图类从控件类继承时,WTL就会使用这一技术.
在notification消息和子类化这个主题上,有一点非常值得指出,那就是当某事件发生时,绝大多数窗体控件都会发送notification消息给它们的父窗体.让你窗体来处理这些notification消息要比子类化一个已存在控件窗体(或子类化一个已存在的类,然后建立一个实例),从而在控件之前取得消息好得多. 譬如, 你想处理button的click事件,你所须要做的仅仅是处理BN_CLICKED notification.它将由button发送给你的窗体类.另外的一种方法是从CContainedWindow<>子类化BUTTON窗体来处理click消息.
我之所以说这个是由于一个知名的ATL鼓吹者给我一份代码里就是这么做的.他的代码取得一个简单的buttonclick事件所花的时间是别人的3到4倍,由于他子类化了button控件,而不是简单的处理BN_CLICKED notification.
WTL还提供了一些新的控件,在win32中没有对等者. 你已经看到过一个 -- command bar, 实际上还有其它一些非常实用类:
类 |
描写叙述 |
CBitmapButton |
这是一个用位图替代标题的button.你能够提供一个image list,里边包括button在正常状态,失效, 推入和鼠标落在button上的图表. |
CHyperLink |
让你建立一个static控件,它代表一个hyperlink,这样当用户点击它时,默认的web浏览器打开该链接. |
CWaitCursor |
这只是是在它的构造函数中把鼠标图标改成等待状态,而在析构函数中还原. |
CCheckListViewCtrl |
在每一项边上都有一个check box的list box. |
CMultiPaneStatusBarCtrl |
具有多个pane的状态条 |
|