创建有个性的对话框之ATL/WTL篇
前记
这几个嵌入类其实很早之间就完成了,2003年的时候我在codeproject上发布了这些代码,不过当时使用了紫色作为按钮的边框,导致几个无聊的ly在哪里争吵关于gays的问题,呵呵,看来不仅语言要接轨,颜色的认识也要和西方接轨哟。现在刚好趁这个机会整理一下代码,写篇文章纪念之。
本文的目的是使用atl/wtl做一个与“创建有个性的对话框之mfc篇”的个性对话框一样的对话框。atl/wtl一套模板库,创建atl/wtl应用程序不可避免的要用到c++的模板与多继承方面的知识,在看本文之前希望您对它们有所了解。本文结尾可以下载文中介绍的例子代码,编译这些代码需要安装wtl库,对于wtl的更详细的信息请查看orbit翻译的“wtl for mfc programmers”系列文章,具体位置在:http://blog.csdn.net/orbit。
atl和wtl一起构建了一个轻量级的应用程序框架,atl在设计时接口定义和实现是严格区分开的,这在窗口类的设计中是最明显的,这一点类似于com,com的接口定义和实现是完全分开的(或者可能有多个实现)。atl有一个专门为窗口设计的接口,可以做全部的窗口操作,这就是cwindow。它实际上就是对hwnd操作的包装类,对几乎所有以hwnd句柄为第一个参数的窗口api的进行了封装,例如:setwindowtext() 和 destroywindow()。在atl类中对窗口过程的实现是cwindowimpl。cwindowimpl 含有所有窗口实现代码,例如:窗口类的注册,窗口的子类化,消息映射以及基本的windowproc()函数,可以看出这与mfc的设计有很大的不同,mfc将所有的代码都放在一个cwnd类中。
由于atl和mfc都是应用于windows平台的库,所以他们都能够响应和处理系统发送的窗口消息,只是atl和mfc对消息的分派方式不同,从而造成编写代码方面的差异。这些差异并不是不可逾越,atl也定义了一些与mfc类似的宏来处理和分派消息,每个atl的窗口类都用一个消息映射表或者称其为消息映射链,将消息处理函数与特定的消息关联起来,这和mfc的做法是类似的。少量的不同之处在于消息响应函数的参数,mfc对windows的消息参数,也就是wparam和lparam进行了内部解释,传递给消息响应函数的参数比较友好,atl的消息响应函数则是原原本本的将wparam和lparam传递给了消息响应函数,对windows的消息不太熟悉的程序员可能会很迷惑。如果你对c++的模板机制比较了解,并且愿意不停的查阅msdn,那就很容易将mfc的窗口类“翻译”成atl/wtl的窗口类,比如本文用到的cwzbuttonimpl类就是从本文的“mfc姊妹篇”中例子代码的csmbutton类翻译过来的。
在开始用atl/wtl创建个性对话框之前,还要介绍一下atl中常用的嵌入类(mix-in class)。atl的另一个显著不同之处就是任何一个c++类都可以响应消息,而mfc只是将消息响应任务分给了cwnd类和ccmdtarget类,外加几个有pretranslatemessage()方法的类。atl的这种特性允许我们编写所谓的“嵌入类”,为我们的窗口添加特性只需将该类添加到继承列表中就行了,就这么简单!一个基本的带有消息映射链的类通常是模板类,将派生类的类名作为模板的参数,这样它就可以访问派生类中的成员,比如m_hwnd(cwindow类中的hwnd成员)。让我们来看一个嵌入类的例子,这个嵌入类通过响应wm_erasebkgnd消息来画窗口的背景:
template
class cpaintbkgnd : public cmessagemap
{
public:
cpaintbkgnd() { m_hbrbkgnd = createsolidbrush(t_crbrushcolor); }
~cpaintbkgnd() { deleteobject ( m_hbrbkgnd ); }
begin_msg_map(cpaintbkgnd)
message_handler(wm_erasebkgnd, onerasebkgnd)
end_msg_map()
lresult onerasebkgnd(uint umsg, wparam wparam, lparam lparam, bool& bhandled)
{
t* pt = static_cast(this);
hdc dc = (hdc) wparam;
rect rcclient;
pt->getclientrect ( &rcclient );
fillrect ( dc, &rcclient, m_hbrbkgnd );
return 1; // we painted the background
}
protected:
hbrush m_hbrbkgnd;
};
让我们来研究一下这个新类。首先,cpaintbkgnd有两个模板参数:使用cpaintbkgnd的派生类的名字和用来画窗口背景的颜色。(t_ 前缀通常用来作为模板类的模板参数的前缀)cpaintbkgnd也是从cmessagemap派生的,这并不是必须的,因为所有需要响应消息的类只需使用begin_msg_map宏就足够了,所以你可能看到其他的一些嵌入类的例子代码,它们并不是从该基类派生的。
构造函数和析构函数都相当简单,只是创建和销毁windows画刷,这个画刷由参数t_crbrushcolor决定颜色。接着是消息映射链,它响应wm_erasebkgnd消息,最后由响应函数onerasebkgnd()用构造函数创建的画刷填充窗口的背景。要在我们的窗口中使用这个嵌入类需要做两件事:首先,将它加入到继承列表:
class cmywindow : public cwindowimpl,
public cpaintbkgnd(www.iocblog.net 文章来源)
其次,需要cmywindow将消息传递给cpaintbkgnd,就是将其链入到消息映射链,在cmywindow的消息映射链中加入chain_msg_map宏:
typedef cpaintbkgnd cpaintbkgndbase;
begin_msg_map(cmywindow)
message_handler(wm_close, onclose)
message_handler(wm_destroy, ondestroy)
command_handler(idc_about, onabout)
chain_msg_map(cpaintbkgndbase)
end_msg_map()
任何cmywindow没有处理的消息都被传递给cpaintbkgnd。应该注意的是wm_close,wm_destroy和idc_about消息将不会传递,因为这些消息一旦被处理消息映射链的查找就会中止。你可以在继承列表中使用多个嵌入类,每一个嵌入类使用一个chain_msg_map宏,这样消息映射链就会将消息传递给它。这与mfc不同,mfc的cwnd派生类只能有一个基类,mfc自动将消息传递给基类。
了解了atl消息处理方式和嵌入类的知识之后,就可以开始创建atl/wtl的彩色对话框了。在“创建有个性的对话框之mfc篇”中提到了一种最简单的改变对话框背景颜色的方法,就是调用cwinapp::setdialogbkcolor()函数,很不幸,atl/wtl没有提供这个方法,至少atl 7.1和wtl 7.1是这样的。不过没关系,我们可以向mfc那样通过处理一些特殊的窗口绘制消息来实现彩色的对话框,而且atl/wtl的方式更加灵活,稍后就会看到。
首先是修改对话框的背景,这个只需要处理wm_erasebkgnd消息就行了,在atl的窗口中也可以这样做,不过atl可以使用更加灵活的嵌入类,前面提到的cpaintbkgnd就是这样的嵌入类。使用嵌入类将wm_erasebkgnd消息的处理函数封装到cpaintbkgnd类中,避免了cmaindlg代码的过度臃肿,同时也使得创建同样背景的对话框更加用以,不需要拷贝onerasebkgnd中的代码到新对话框类,只需将cpaintbkgnd加入到新类的集成列表中就行了:
class cmaindlg : public cdialogimpl
public cpaintbkgnd
Tag: ATL ,WTL
文章整理:iocblog
版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。