先来说一下我学习这个东西的目的吧。
刚开始接触ATL和WTL是在11月份的时候,在公司的buffer里面待了无聊的两个月,也没有项目做。对于我这个刚毕业又不是计算机专业,又找到了IT行业的菜鸟来说,没有学习的目标其实挺悲惨的,一度什么东西都学,js,java。已经在公司混了大概4,5个月了,一点长劲都没有,前途堪忧。
虽然我学的不是计算机专业,但好歹是电子信息学院的,本科研究生都接触了点编程语言,尤其在找工作的时候疯狂地补了一下c++相关知识,所以对c/C++这方面的基础知识还是有些了解的。但是苦于没有项目经验。想着我要回成都,这心里就没底啊。
还好有个外派到思科的职位,被我争取来了,是做Windows下的产品维护。就是人家这个东西已经做好了,现在已经在做IOS的项目了,现在Windows下可能会报出些bug来,就需要我来修复。在准备面试的时候了解了一下关于ATL和COM方面的知识,不过现在都忘了。之前遇到一个bug,就是有一个客户在点击其他地方的时候,我们的程序会跳出一个空白的对话框,并且不会消失。这属于business card方面的知识,我查了好久也没确定出什么原因。最后发现了一个不确定的原因,可能是这个空白对话框没有隐藏,被系统调出来了。经历过这次查bug,发现了整个逻辑应该是什么样子的。所以自己也想做一个聊天工具的东西,看到他们应该是使用atl/wtl来做的,我就再来复习一下这方面的东西。
今天看懂啊第四章了,把前面atl的知识也梳理一下吧。
ATL是用来做UI的,不知道这句话说的是不是狭隘了。其中的CWindow封装了m_hwnd成员变量,把创建一个窗口,显示窗口,消息循环的API函数全封装进了这个类中,从而更加面向对象。
ATL力争做到简洁,快速,这也是它为什么采用了模板类来做的原因。去掉了vtable的开销,并且最大限度地在编译器确定调用的类型,这就是子类作为父类模板参数传递的原因,其中最重要的一句是:T* pt = static_cast<*T>(this);
了解了这些,然后再了解一些模板类的概念,基本上ATL就这些了,接下来就是要对它封装的类和宏的熟练应用了,这就需要长期的积累了。
但是ATL对一些复杂的控件的支持不足,所以就出现了WTL,我不知道对不对哈,这句话先放在这里,以后再改。
好了,现在进入今天的正题:
MFC程序员的WTL指南:PartIV--对话框与控件的学习
在说这个之前,先说一个小插曲。这个故事是发生在我查上述的bug的时候,business card就是相当于qq上把鼠标放在好友头像上,旁边弹出的一个对话框,上面有简单的个人信息。
既然是个对话框,肯定有个资源与之对应,该资源必须应该有个ID,对话框嘛,一般就是IDD_DIALOG之类的。但是我在该工程里面到处都没有发现哪里调用了domodal()这个方法。后来去了基类里面查,才发现乾坤原来在这个里面。基类中有个idd专门用来接收子类的IDD,注意这个必须是IDD,IDC,IDE都不行,因为在DialogImpl这个基类中就是这么写的,人家只认这个。然后有了这个id之后,该类还有DoModal()方法,其实是调用了Create()方法。中间还有其他的函数,但这个资源总算是与一个句柄搭上线了。
回到这一章来,我对它所说的创建一个对话框要做的这三件事很有感触,分别是:
1. 创建一个对话框资源,就是你要弹出来让别人看的东西。
2. 从CDialogImpl类派生一个新类,处理一些自己想处理的消息,添加一个空间。
3. 添加一个共有成员变量IDD,其实是enum{IDD=IDD_DIALGO};
然后这章就主要在讲怎么把一个类对象同一个资源进行连接了。
原来我就对怎么连接这两个东西比较有兴趣,你想啊,一个是你自己编写的一个类,一个是你画出来的资源。比如一个CMyButton类,一个按钮,它们两个根本不是一个东西,CMyButton类中只是有些方法,成员,但是能干什么呢?你看不到摸不着,不跟一个资源连接起来你是看不到它是怎么工作的。但是一个按钮又是一章图而已,它没有什么状态,也没有行为,按它也没有反应,所以就要把两者结合起来,要看得到,还要有反应。
我觉得CMyButton类完全可以跟一个对话框连接,没做过实验,不知道会出现什么结果。
ATL中有三种方法:
1. 先获得资源,HWND hwnd = GetDlgItem(IDC_BUTTON),好了,这个叫IDC_BUTTON的东西在内存中已经存在了,并且通过这个hwnd可以找到它了,相当于hwnd是它的秘书或者发言人。那我要把这个按钮同一个对象连接起来,我肯定是要通过它秘书跟它建立关系啦。这里面就有几个方法。
1.1 CButton wndBtn1(hwnd)
1.2 CButton wndBtn2 = hwnd;
1.3 CButton wndBtn3;
wndBtn3.Attach(hwnd)//其实你去看这个Attach()方法,就是把hwnd给m_hwnd,跟前面两种相差不大
2. 包容器窗口
这个我只是按教程实现了,但是我没有深究,可以子类化一个控件,可以把传给父窗口的消息截留,自己先处理,处理完可以选择不传给父窗口处理,也可以选择继续让父窗口处理。
子类化的问题我没有搞清楚,大概就是这个控件已经有了,现在你自己搞了一个相应的类,你把这个控件和类联系起来了。这跟没说一样。
3. 就是子类化了
就是SubClassWindow()这个函数,我看我们的程序中用的都是这个,而且也比较简单。子类化一个空间可以把控件上的消息让控件自己处理,不用全交给父类,那样老爹就太累了,况且,父类窗口上又多了很多控件,如果都让父类处理,太不人道了。还是自己的事情自己做。
所以你就要有个相应的类,并且需要从CWindowImpl继承,这样才能做消息循环嘛,还得在CWindowImpl<>模板参数中写上你这个类的相应的控件基类。怎么说呢,就是如果你要子类化一个ListContrl就得这个搞个类:
CMyListImpl:public CWindowImpl<CMyListImpl, CControlView>,类模板的第一个参数不用说了,第二个就是相应的控件基类了。
在这个类里面放上你想处理的消息,就成了。
然后在CMainDlg.h文件中的CMainDlg类中定义一个对象,就是你要跟那个资源连接的东西CMyListImpl wndList;
还要在Dialog的OnInit函数里面写上 wndList.SubClassWindow(GetDlgItem(IDC_LIST));就成了。
我感觉这种方法简单直观。
再有就是WTL的DDX了,其实就是WTL定义了几个宏,其基本思想还是用SubClassWindow这个方法
首先,你的CMainDlg得继承CWinDataChange〈CMainDlg〉 如果你没这个的东西,你就用不了这里面的BEGIN_DDX_MAP_EX()宏,等会我分解一下这个宏,来看看里面到底有点啥,其实就是让你重载一个叫DoDataExchange()的方法。
#define BEGIN_DDX_MAP(thisclass)\
BOOL DoDataExchange(BOOL bSaveAndValidate = FALS,\
UINT cltlID = (UINT)-1)\
{\
bSaveAndValid;\
//数据传输方向标志,true代表从//控件传至变量
nCtrlID;
#define DDX_CONTROL(nID, obj)\
if(nCtrlID == (UINT)-1 || nCtrlID == nID)\
DDX_Control(nID, obj, bSaveAndValidate);
template<
class T>
class CWinDataExchange
{
.
template<
class TContrl>
void DDX_Control(UINT nID, TControl& ctrl, BOOL bSave)
{
if(!bSave&&ctrl.m_hwnd==NULL)\\必须从CWindImpl继承才有这个变量
{
T* pT = static_cast<T*>(
this);
ctrl.SubClassWindow(pT->GetDlgItem(nID));
//这个T在这里就是CMainDlg啦,神奇吧
}
}
#define END_DDX_MAP()\
return TRUE;\
}
看了上面的解释基本能了解DDX其实跟上面差不多把,就是用宏把他们整合起来了,障眼法。
额,下面就是控件的反射了,据说MFC都是把传到父类的消息反射回去,谁给我的我反给谁,挺懒的。
WTL也有几个宏来实现这个目的,并且谁给我的我都知道,原封不动地给你。额,现在用不到,理解不深,就先写到这里吧。
posted on 2012-03-22 19:54
Dino-Tech 阅读(406)
评论(0) 编辑 收藏 引用