2. 其他的控件可以显示数据,但只有窗体类控件才能和用户发生键盘交互。
3. 窗体类控件能够包含其他控件(容器)。
4. 包含其他控件的控件又称做父控件。只有窗体类控件才能够作为其他控件的父控件。
5. 窗体类控件拥有句柄。
除了能够接受焦点之外,twincontrol的一切都跟tcontrol没什么分别。这一点意味着twincontrol可以对许多的标准事件作出响应,windows也必须为它分配一个句柄。并且与这个主题相关的最重要的是,这里提到是由bcb负责来对控件进行重画以及消息处理。这就是说,twincontrol封装了这一切。
似乎扯的太远了。但我要提出来的问题是tcontrol类的wndproc方法中处理了鼠标消息。但这个消息只有它的子类twincontrol才能够得到啊!?
这怎么可以呢... borland是如何实现这一切的呢?这个问题实在很奥妙。为了看个究竟,再次深入vcl吧。
还是在control.pas中,twincontrol继承了tcontrol的wndproc方法。源码如下:
procedure twincontrol.wndproc(var message: tmessage);
var
form: tcustomform;
keystate: tkeyboardstate;
wheelmsg: tcmmousewheel;
begin
case message.msg of
wm_setfocus:
begin
form := getparentform(self);
if (form <> nil) and not form.setfocusedcontrol(self) then exit;
end;
wm_killfocus:
if csfocusing in controlstate then exit;
wm_nchittest:
begin
inherited wndproc(message);
if (message.result = httransparent) and (controlatpos(screentoclient(
smallpointtopoint(twmnchittest(message).pos)), false) <> nil) then
message.result := htclient;
exit;
end;
wm_mousefirst..wm_mouselast:
//下面这一句话指出,鼠标消息实际上转入iscontrolmousemsg方法来处理了。
if iscontrolmousemsg(twmmouse(message)) then
begin
if message.result = 0 then
defwindowproc(handle, message.msg, message.wparam, message.lparam);
exit;
end;
wm_keyfirst..wm_keylast:
if dragging then exit;
wm_cancelmode:
if (getcapture = handle) and (capturecontrol <> nil) and
(capturecontrol.parent = self) then
capturecontrol.perform(wm_cancelmode, 0, 0);
else
with mouse do
if wheelpresent and (regwheelmessage <> 0) and
(message.msg = regwheelmessage) then
begin
getkeyboardstate(keystate);
with wheelmsg do
begin
msg := message.msg;
shiftstate := keyboardstatetoshiftstate(keystate);
wheeldelta := message.wparam;
pos := tsmallpoint(message.lparam);
end;
mousewheelhandler(tmessage(wheelmsg));
exit;
end;
end;
inherited wndproc(message);
end;
鼠标消息是由iscontrolmousemsg方法来处理的。只有再跟到iscontrolmousemsg去看看啦。源码如下:
function twincontrol.iscontrolmousemsg(var message: twmmouse): boolean;
var
//tcontrol出现啦
control: tcontrol;
p: tpoint;
begin
if getcapture = handle then
begin
control := nil;
if (capturecontrol <> nil) and (capturecontrol.parent = self) then
control := capturecontrol;
end else
control := controlatpos(smallpointtopoint(message.pos), false);
result := false;
if control <> nil then
begin
p.x := message.xpos - control.left;
p.y := message.ypos - control.top;
file://tcontrol的perform方法将消息交由wndproc处理。
message.result := control.perform(message.msg, message.keys, longint(pointtosmallpoint(p)));
result := true;
end;
end;
原来如此,twincontrol最后还是将鼠标消息交给tcontrol的wndproc来处理了。这里出现的perform方法在bcb的帮助里可以查到是tcontrol类中开始出现的方法。它的作用就是将指定的消息传递给tcontrol的wndproc过程。
结论就是tcontrol类的wndproc方法的消息是由twincontrol类在其重载的wndproc方法中调用iscontrolmousemsg方法后使用peform方法传递得到的。
由于这个原因,bcb和delphi中的tcontrol类及其所有的派生类都有一个先天的而且是必须的限制。那就是所有的tcontrol类及其派生类的owner必须是twincontrol类或者twincontrol的派生类。owner属性最早可以在tcomponent中找到,一个组件或者控件是由它的owner拥有并负责释放其内存的。这就是说,当owner从内存中释放的时候,它所拥有的所有控件占用的内存也都被释放了。owner最好的例子就是form。owner同时也负责消息的分派,当owner接收到消息的时候,它负责将应该传递给其所拥有的控件的消息传递给它们。这样这些控件就能够取得处理消息的能力。timage就是个例子:你可以发现borland并没有让timage重载tcontrol的wndproc方法,所以timage也只有处理鼠标消息的能力,而这种能力正是来自tcontrol的。
唧唧崴崴的说了一大堆。终于可以说处理消息的第二种方法就是重载tcontrol的wndproc方法了。例程如下: