深入VCL 理解BCB的消息机制(二)[2]

[入库:2005年8月19日] [更新:2007年3月24日]

本文简介:选择自 cker 的 blog

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方法了。例程如下:

本文关键:VCL 消息 CKER
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top