大家都知道,很多对话框的CStatic框上实现了超链接的效果(下划线,字体颜色变化,鼠标悬浮变手型)。
因为是照着案例敲代码,所以不懂这到底因为什么,不过具体是这样实现的。
继承类CStatic得到类CHyPerLinker类。
然后添加消息处理函数OnLButtonDown,OnMouseMove,OnSetCursor
CtlColor
修改虚函数PreSubclassWindow
添加设置属性函数SetAttribute
SetAttribute就不解释了,就是设置一下网址,颜色等等。
OnLButtonDown,OnMouseMove,OnSetCursor是处理消息函数。
重点在于PreSubclassWindow和CtlColor。
PreSubclassWindow函数的官方解释
框架调用这个成员函数以允许在窗口被之前进行其它必要的子类化。重载这个函数以允许控件的动态子类化。
什么是动态子类化,SubclassWindow实现的就是动态子类化
PreSubclassWindow在cwnd中两个地方被调用SubclassWindow和LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)。
查看源代码,我们发现,这个SubclassWindow函数基本只干了两件事,
PreSubclassWindow(); //我个人认为这个函数是设置窗口的一些style
// now hook into the AFX WndProc
WNDPROC* lplpfn = GetSuperWndProcAddr();
WNDPROC oldWndProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC,
(INT_PTR)AfxGetAfxWndProc());
ASSERT(oldWndProc != AfxGetAfxWndProc()); //第二呢,就是把窗口默认的消息处理函数做个替换
其实MFC的控件类就进行了子类化,因为控件是微软早就封装好的,即使你使用SDK编程,也可以一句话就创造一个完美的Edit控件,但是众所周知,MFC类的处理函数是AfxWindowProc,所以就利用HOOK进行了子类化。
至于有些人通过classWizard将控件和对象建立联系,其实这个已经内部完成了子类化,如果不用wizard的话就需要手动子类化(其实也很简单)。
控件和变量的数据交换是通过DDX完成的。
个人理解的,这些宏将控件和变量建立联系,并没有子类化,而是单纯的将m_Hwnd赋值,没有经过createEx函数,所以没有用HOOK子类化,还不在你的控制范围呢,需要用subckasswindow人为的子类化(当然,wizard做了,不需要我们完成)。
有
些人可能不理解了,直接SetWindowLong就可以,为什么还要用HOOK呢?好吧,我想问问你,这个SetWIndowLong应该在哪里完
成? 此时控件不在你的控制下,只有父窗口的Dialog在你的控制下,
当然对于子控件的控制权时越早获得越好,所以你可能想要在Dialog初始化的时候就进行,但是....Dialog的初始函数DialogBox也是
windows写死的,当你的dialog完成时候,已经有好多消息被子控件获取并处理了,比如WM_PAINT,这时候你的SetWindowLong
就显得晚了(比如你就是想重绘,处理WM_PAINT)。
那PreSubclassWindow是干什么的呢?其实也就是设置下这个窗口的style之类,准备工作啦。
这样貌似应该懂了,其实我也有些迷糊,不过先记这些,不影响使用就OK了。
那消息的反射呢?控件的好多消息自己不处理,比如WM_CTLCOLOR,在重绘前获取画刷信息,控件是发送消息给父窗口处理的。但这很显然不符合面向对象的特征,而且我们现在已经完全获取了子控件的控制权,什么都可以处理了,来吧,发给自己吧。
所以有了WM_CTLCOLOR_REFLECT,由父窗口再发送给子控件,处理完了如果返回True则结束,如果是False就说明子控件没有处理,再有父窗口处理。
CtlColor就是这个反射消息对应的函数