方法1:使用消息映射(MessageMap)重载TObject的Dispatch虚成员函数
在\Borland\CBuilder5\Include\Vcl找到sysmac.h,其中有如下的预编译宏定义:
#define BEGIN_MESSAGE_MAP virtual void __fastcallDispatch(void *Message)\
{ \
switch(((PMessage)Message)->Msg) \
{
#define VCL_MESSAGE_HANDLER(msg,type,meth) \
case msg: \
meth(*((type *)Message)); \
break;
//NOTE:ATL defines a MESSAGE_HANDLER macro which conflicts
{
switch(((PMessage)Message)->Msg)
{
//NOTE:ATL
//with VCL's macro. The VCL macro has been renamed to VCL_MESSAGE_H//ANDLER. If you are not using ATL, MESSAGE_HANDLER is definedas
//in previous versions of BCB.
#if !defined(USING_ATL)&&!defined(USING_ATLVCL)&& !defined (INC_ATL_HEADERS)
#define MESSAGE_HANDLER VCL_MESSAGE_HANDLER
#endif // ATL_COMPAT
#define END_MESSAGE_MAP(base)
default: \
base: ispatch(Message); \
break; \
} \
}
方法2:重载TControl的WndProc方法
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方法。反而提供了一个新的方法
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消息.
言归正传,在帮助的The
由于这个原因,BCB和Delphi中的TControl类及其所有的派生类都有一个先天的而且是必须的限制。那就是所有的TControl类及其派生类的Owner必须是TWinControl类或者TWinControl的派生类。Owner属性最早可以在TComponent中找到,一个组件或者控件是由它的Owner拥有并负责释放其内存的。这就是说,当Owner从内存中释放的时候,它所拥有的所有控件占用的内存也都被释放了。Owner最好的例子就是Form。Owner同时也负责消息的分派,当Owner接收到消息的时候,它负责将应该传递给其所拥有的控件的消息传递给它们。这样这些控件就能够取得处理消息的能力。TImage就是个例子:你可以发现Borland并没有让TImage重载TControl的WndProc方法,所以TImage也只有处理鼠标消息的能力,而这种能力正是来自TControl的。