龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > C/C++开发 >

深入VCL理解BCB的消息机制2

时间:2009-12-22 15:42来源:未知 作者:admin 点击:
分享到:
重载TControl的WndProc方法 还是先谈谈VCL的继续策略。VCL中的继续链的顶部是TObject基类。一切的VCL组件和对象都继续自TObject。 打开BCB帮助查看TControl的继续关系: TObject->TPersistent->TCompon

  重载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的实现在BorlandCBuilder5SourceVclcontrol.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. 可以在程序运行时取得焦点的控件。

  

  

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_NCH99vTEST:

  

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;

  

en

  

精彩图集

赞助商链接