WTL体系结构Word下载.docx
- 文档编号:21733405
- 上传时间:2023-02-01
- 格式:DOCX
- 页数:11
- 大小:79.96KB
WTL体系结构Word下载.docx
《WTL体系结构Word下载.docx》由会员分享,可在线阅读,更多相关《WTL体系结构Word下载.docx(11页珍藏版)》请在冰豆网上搜索。
Toolbar"
选项,你可以通过"
Rebar"
选择是否将工具条放入IERebar控件中.如果你选取了Rebar,你就可以通过框架窗口(framewindow)的成员m_hWndToolBar(后边会有详细的描述)来访问它.你可以按照你的意愿,在里边加入其他的工具条.选取了"
后,你可以决定是否选取"
CommandBar"
.Commandbar很像CE的commandbar控件.只是WTL是用一个类来实现,而在CE,commandbar是一个系统窗口类(systemwindowclass).Commandbar非常有用,它能够把窗口也加入到工具条中去.如果你选取了这个选项,工具条和菜单都将被当做toolbar来实现.这使菜单项也可以有关联的图标,并且当你移动鼠标到一个菜单项上时,该菜单项会被置成高亮.从Office97以来,Office软件的菜单都具有上述特征.
第二页还有指定程序是否使用视的选项(多半你想要使用),同时你可以决定这些视如何实现.下表列出了所有可选的视.
视
GenericWindow
一个简单的窗口.此类窗口允许程序员编写WM_PAINT消息的处理函数.适用于需要直接进行paint的文档.
Form
这类视具有一个对话框模版.适用于带ActiveX控件的窗口.应用程序来操作这些控件.
ListBox
这个视是个listbox.它最简单的形式意味着可以通过调用AddString()方法来添加字符串.
Edit
这个视是个editcontrol.本质上,它提供了一个像Notepad一样的程序.
ListView
这个视是个listview通用控件.用这个控件来显示相关的项(比如,控制面板是一个Explorer主持的ListView,所有的项都是控制面板applet).
TreeView
这个视是个treeview通用控件.这个适用于具有层次关系的数据,比如,可以用它来显示数据库的schema.顶层分支为表和存储过程,次级的分支为表中的字段.
RichEdit
这个视是个richedit控件,像WordPad.
HTMLPage
这个视主持了一个IEWebBrowser控件.它把主持的一个webpage当成一个视.
本文的例子需要一个对话框模版,同时还需要菜单,因此Formview是个理想的选择.
程序线程
跟ATL一样,WTL程序也需要一个_Module全局变量来保存全局数据,方便应用级代码访问.在WTL中,这个变量是CAppModule或CServerAppModule的实例,后者在程序同时作为一个COM服务器时用到.每个应用程序具有一个或者多个UI线程.WTL使用两种方式来管理这些线程.
如果应用程序只有一个UI线程(除了多线程SDI以外,其他程序类型默认只有一个UI线程),线程调用全局函数run():
intRun(LPTSTR/*lpstrCmdLine*/=NULL,intnCmdShow=SW_SHOWDEFAULT)
{
CMessageLooptheLoop;
_Module.AddMessageLoop(&
theLoop);
CMainFramewndMain;
if(wndMain.CreateEx()==NULL)
{
ATLTRACE(_T("
Mainwindowcreationfailed!
\n"
));
return0;
}
wndMain.ShowWindow(nCmdShow);
intnRet=theLoop.Run();
_Module.RemoveMessageLoop();
returnnRet;
}
线程的消息循环包含在CMessageLoop内部.函数创建了一个CMessageLoop实例,把它放入全局的消息循环映射(messageloopmap)数组.以线程ID为索引,线程中运行的其他的代码可以访问到这个实例.消息循环对象包含了messagefilter和idlehandler.运行在这个UI线程的UI元件(UIelement)可以有它自己的idlehandler,在线程的消息队列为空时运行【译注:
通过CMessageLoop:
:
AddIdleHandler()把这个UI元件加入到CMessageLoop的idlehandler数组中】.CMessageLoop:
Run()包含了UI线程的主消息映射(mainmessagemap).下边是它的伪代码:
MSGm_msg;
intCMessageLoop:
Run()
for(;
;
)
while(!
PeekMessage(&
m_msg,NULL,0,0,PM_NOREMOVE))
DoIdleHandlers();
bRet=:
GetMessage(&
m_msg,NULL,0,0);
if(bRet==-1)
continue;
elseif(!
bRet)
break;
if(!
DoMessageFilters(&
m_msg))
:
TranslateMessage(&
m_msg);
DispatchMessage(&
return(int)m_msg.wParam;
可以看到,这个函数推动着消息队列.没有消息时,运行注册到线程的idlehander.如果在队列中检测到消息,把它取出来,传给每个messagefilter.如果消息没有被这些函数处理,它将按照通常的方式,发送到目标窗口.
如果程序有超过一个的UI线程,可以用WTL的线程管理器,多线程SDI就是这样做的.主线程作为一个管理者线程,它会为每个新窗口创建一个新的新线程.主要流程如下:
intnRet=m_dwCount;
DWORDdwRet;
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);
elseif(dwRet==(WAIT_OBJECT_0+m_dwCount))
msg,NULL,0,0);
if(msg.message==WM_USER)
AddThread(_T("
"
),SW_SHOWNORMAL);
那些线程句柄放在一个数组中.线程通过AddThread()加入到数组(同时启动线程),RemoveThread()从数组移走.wait语句在两种情况下会被打断:
线程死亡(将线程从数组中移出)或线程收到了WM_USER消息(一个线程在一个新线程里新建了一个窗口).线程管理者为程序中的一个类,因此可以在循环中加入自己的messagehandler,比如,当程序有不止一种窗口类型时.创建一个新的窗口非常简单,只需在任意一个窗口中调用:
PostThreadMessage(_Module.m_dwMainThreadID,WM_USER,0,0L);
这个循环会一直运行下去,直到所有的UI线程都关闭了.UI线程具有一个threadprocedure,它跟单UI线程的Run()方法一样.不过,由于线程管理者使用了MsgWaitForMultipleObjects(),这意味者最多只能有MAXIMUM_WAIT_OBJECTS-1个UI线程,这也意味着最多只能创建63个窗口.
框架
WTL实际上是两类窗口:
框架窗口和视图窗口.正如名字所暗示的那样,框架窗口为窗口提供标题栏(captionbar)和边框,你的代码用它来处理工具条(toolbar)和菜单项命令.你看到的程序窗口实际上是视图窗口,视图覆盖了框架窗口的客户区.客户区是指框架窗口没有被诸如状态条,工具条之类的修饰部件所遮挡的部分.
线程会创建主框架窗口的一个实例,创建视图的工作由主框架窗口的WM_CREATE消息处理函数完成.对于SDI程序来说,这个过程很简单.把视图类的一个实例作为主框架类的一个成员,调用视图类的Create()方法即可.MDI程序稍微有些不同,MDI主框架窗口通过CMDIFrameWindowImpl<
>
CreateMDIClient()建立一个名为MDICLIENT的窗口.这个客户窗口将CMDIChildWindowImpl<
窗口当做它的子窗口,子窗口有一个视图.这也反映了这么一个事实,MDI程序可以具有零个或者多个子窗口,每个都有边框和标题栏.
框架窗口的OnCreate()很有意思,让我看看:
LRESULTOnCreate(UINT,WPARAM,LPARAM,BOOL&
//createcommandbarwindow
HWNDhWndCmdBar=m_CmdBar.Create(m_hWnd,rcDefault,
NULL,ATL_SIMPLE_CMDBAR_PANE_STYLE);
//attachmenu
m_CmdBar.AttachMenu(GetMenu());
//loadcommandbarimages
m_CmdBar.LoadImages(IDR_MAINFRAME);
//removeoldmenu
SetMenu(NULL);
HWNDhWndToolBar=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);
AddIdleHandler(this);
这是从一个SDI程序拿来的一段代码,该程序有一个基于commandbar的工具条和一个状态条.函数的第一行创建了一个commandbar实例,然后对它进行初始化,在其中加入框架窗口的菜单和工具条位图.这段代码先将菜单取出,把所有的下拉菜单转换为工具条按钮,并将菜单保存在一个变量中,以备后用.给人的感觉是菜单是由工具条实现的-那我们就把它叫做工具条菜单(menutoolbar)吧.然后CommandBar将程序工具条的图标装入imagelist并将它们的ID保存在数组中.当点击工具条菜单的按钮时,commandbar会找到对应的子菜单,创建一个弹出菜单.Commandbar将子菜单项的ID和它保存的ID进行比较,这些ID跟imagelist中的工具条按钮图标是相关联的.如果比较成功,则将关联的图标加到菜单项上去.这意味着相同ID的菜单项和工具条按钮具有相同的图标.
接下来,创建工具条并把它关联到commandbar,然后创建状态条和视图.可以看到视图的HWND存放在框架窗口的m_hWndClient变量中.这个窗口句柄在框架窗口的WM_SIZEhandler中会用到.当框架窗口改变大小时,它告知视图改变自身,于此同时也要考虑状态条和commandbar.
在下来的三行(从调用UIAddToolBar()开始)用来显示在运行时会改变状态的UI项(UIitem).文章后面还会重提这个话题.最后,访问消息循环(messageloop),你应该还记得该消息循环存放在一全局数组中.GetMessageLoop()取得当前线程的消息循环,加入框架窗口的messagefilter和idlehandler,分别默认是PreTranslateMessage()和OnIdle().
框架窗口继承于以下类:
classCMainFrame:
publicCFrameWindowImpl<
CMainFrame>
publicCUpdateUI<
publicCMessageFilter,
publicCIdleHandler
后两个抽象类宣称了框架窗口类实现了PreTranslateMessage()和OnIdle().从CUpdateUI<
继承表示框架类支持UIupdatemap.
视图
视图窗口看起来显得很简单:
classCMyView:
publicCWindowImpl<
CMyView>
public:
DECLARE_WND_CLASS(NULL)
BOOLPreTranslateMessage(MSG*pMsg)
pMsg;
returnFALSE;
BEGIN_MSG_MAP(CMyView)
MESSAGE_HANDLER(WM_PAINT,OnPaint)
END_MSG_MAP()
LRESULTOnPaint(UINT,WPARAM,LPARAM,BOOL&
CPaintDCdc(m_hWnd);
//TODO:
Addyourdrawingcodehere
};
上面是一个SDI程序的视图类.多线程SDI和MDI的视图类在本质上也跟这个一样,但他们没有PreTranslateMessage()方法.SDI程序就是使用这个函数,赶在框架类处理消息之前把消息抓住.PreTranslateMessage()在SDI的框架类中的实现是,直接将消息转发给视图类.
这里显示的视图实际上没有做什么工作.你应该自己在OnPaint()函数中加入画出文档内容的代码.如果需要支持输入,如鼠标的点击和键盘的按键,你应该加入相应消息处理函数到类和映射中.可以看到这个窗口是从CWindowImpl<
继承下来的,如果你想让它基于一个Win32控件的话,就应该从定义在AtlCtrls.h文件中某个WTL类继承.
如果想在基于CWindowImpl<
的类里加上滚动条,那么你应该把基类换成CScrollWindowImpl<
同时把消息链给它:
publicCScrollWindowImpl<
typedefCScrollWindowImpl<
parent;
CHAIN_MSG_MAP(parent)
voidDoPaint(CDCHandledc)
};
基类保证窗口具备滚动条,并提供滚动条消息的默认处理.视图类不再有WM_PAINT的处理函数,因为它已被CScrollWindowImpl<
处理.根据滚动条的位置,CScrollWindowImpl<
画出视图相对应的部分.取而代之的是,在你的类里实现DoPaint(),在这里你需要画出整个视图.如果你想指定滚动的范围,大小或起点,你需要加上处理WM_CREATE消息的函数,把这些初始化代码放到里边.
正如我先前所提到的,框架窗口会改变视图窗口的大小,以使它客户区未被状态条和工具条覆盖的部分为视图所填充.在大多数情况下,这样就够了.但是当你想要一个具有WindowsExplorer样子的程序时,该怎么办呢?
WindowsExplorer的窗口包含了一个treeview和一个listview,还有两者之间的分割条.WTL的解决方案很简单:
使用splitter窗口!
为此你需要改变一下框架窗口,让它创建splitter窗口的一个实例作为它的视图.例如,在你的框架类里有如下的数据成员:
CSplitterWindowm_view;
CTreeViewCtrlm_tree;
CListViewCtrlm_list;
你可以在OnCreate()创建一个splitter窗口:
//gettheframeclientrect,sothatwesetthesplitterinitialsize
//andwecangetthesplitterbarinthecentre
RECTrect;
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,
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窗口中去,并确定分割条的位置所在.
UIUpdate
菜单项可以被设置为有效或无效,可以带check记号或着像radio按钮一样,在一组菜单项中同时有且只有一个能被check.此外,菜单项还可以带图标和文字.所有的这些状态都可以在运行时根据程序中的某个值进行改变.工具条在某种程度上可以看做是菜单的易见形态,因为它们的按钮可以个别地,或者作为一组的一部分被置成有效或无效,推入推出.UIupdate机制允
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- WTL 体系结构