|
vcl 理解bcb的消息机制 | |
|
方法2 |
written by |
方法2。重载tcontrol的wndproc方法
还是先谈谈vcl的继承策略。vcl中的继承链的顶部是tobject基类。一切的vcl组件和对象都继承自tobject。
打开bcb帮助查看tcontrol的继承关系:
tobject->tpersistent->tcomponent->tcontrol
呵呵,原来tcontrol是从tpersistent类的子类tcomponent类继承而来的。tpersistent抽象基类具有使用流stream来存取类的属性的能力。
tcomponent类则是所有vcl组件的父类。
这就是所有的vcl组件包括您的自定义组件可以使用dfm文件存取属性的原因『当然要是tpersistent的子类,我想您很少需要直接从tobject类来派生您的自定义组件吧』。
tcontrol类的重要性并不亚于它的父类们。在bcb的继承关系中,tcontrol类的是所有vcl可视化组件的父类。实际上就是控件的意思吧。所谓可视化是指您可以在运行期间看到和操纵的控件。这类控件所具有的一些基本属性和方法都在tcontrol类中进行定义。
tcontrol的实现在\borland\cbuilder5\source\vcl\control.pas中可以找到。『可能会有朋友问你怎么知道在那里?使用bcb提供的search -> find in files很容易找到。或者使用第三方插件的grep功能。』
好了,进入vcl的源码吧。说到这里免不了要抱怨一下borland。哎,为什么要用pascal实现这一切.....:-(
tcontrol继承但并没有重写tobject的dispatch()方法。反而提供了一个新的方法就是xycleo提到的wndproc()。一起来看看borland的工程师们是怎么写的吧。
procedure tcontrol.wndproc(var message: tmessage);
var
form: tcustomform;
begin
//由拥有control的窗体来处理设计期间的消息
if (csdesigning in componentstate) then
begin
form := getparentform(self);
if (form <> nil) and (form.designer <> nil) and
form.designer.isdesignmsg(self, message) then exit;
end
//如果需要,键盘消息交由拥有control的窗体来处理
else if (message.msg >= wm_keyfirst) and (message.msg <= wm_keylast) then
begin
form := getparentform(self);
if (form <> nil) and form.wantchildkey(self, message) then exit;
end
//处理鼠标消息
else if (message.msg >= wm_mousefirst) and (message.msg <= wm_mouselast) then
begin
if not (csdoubleclicks in controlstyle) then
case message.msg of
wm_lbuttondblclk, wm_rbuttondblclk, wm_mbuttondblclk:
dec(message.msg, wm_lbuttondblclk - wm_lbuttondown);
end;
case message.msg of
wm_mousemove: application.hintmousemessage(self, message);
wm_lbuttondown, wm_lbuttondblclk:
begin
if fdragmode = dmautomatic then
begin
beginautodrag;
exit;
end;
include(fcontrolstate, cslbuttondown);
end;
wm_lbuttonup:
exclude(fcontrolstate, cslbuttondown);
end;
end
// 下面一行有点特别。如果您仔细的话会看到这个消息是cm_visiblechanged.
// 而不是我们熟悉的wm_开头的标准windows消息.
// 尽管borland没有在它的帮助中提到有这一类的cm消息存在。但很显然这是bcb的
// 自定义消息。呵呵,如果您对此有兴趣可以在vcl源码中查找相关的内容。一定会有不小的收获。
else if message.msg = cm_visiblechanged then
with message do
senddocknotification(msg, wparam, lparam);
// 最后调用dispatch方法。
dispatch(message);
end;
看完这段代码,你会发现tcontrol类实际上只处理了鼠标消息,没有处理的消息最后都转入dispatch()来处理。
但这里需要强调指出的是tcontrol自己并没有获得焦点focus的能力。tcontrol的子类twincontrol才具有这样的能力。我凭什么这样讲?呵呵,还是打开bcb的帮助。很多朋友抱怨bcb的帮助实在不如vc的msdn。毋庸讳言,的确差远了。而且这个帮助还经常有问题。但有总比没有好啊。
言归正传,在帮助的the twincontrol branch 分支下,您可以看到关于twincontrol类的简介。指出twincontrol类是所有窗体类控件的基类。所谓窗体类控件指的是这样一类控件:
1. 可以在程序运行时取得焦点的控件。