WTL介绍 WTL简单介绍 WTL体系结构

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)

vcmfc

      在ATL出现的时候,一些部分COM的编程人员開始认为开发COM运用是一种快乐,由于使用它非常方便地开发小规模的COM组件,但好景不长,现实的COM组件是包罗相当广泛的,特别当它们准备使用包装我窗体控件,发现ATL提供了相当的稀少。因此Microsoft推出了半成品与没有技术支持的WTL,这也是WTL诞生的原因。

      非常多初次接触WTL都问“WTL这三个字母代表什么呢?”:WTL全称为Windows Template Library,构架于ATL之上,採用C++模板技术来包装大部窗体控制,并给出一个与MFC类似的应用框架。

       他们紧跟着问“那我怎样得到它呢?”:因为WTL是Microsoft推出的,在Microsoft的PlatForm SDK中就有,下面是部分画面:

WTL介绍
WTL简单介绍
WTL体系结构

或者能过下面链接下载: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介绍
WTL简单介绍
WTL体系结构

 

    下表对这些应用程序进行了描写叙述. 这样的弹性构成了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).

WTL介绍
WTL简单介绍
WTL体系结构

    假设选取了"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中,这个变量是CAppModuleCServerAppModule的实例,后者在程序同一时候作为一个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 centre
RECT 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的状态条