Delphi TControl 类(2)消息

页面导航:首页 > 软件编程 > Delphi > Delphi TControl 类(2)消息

Delphi TControl 类(2)消息

来源: 作者: 时间:2016-01-18 15:41 【

Delphi--TControl与Windows消息的封装TControl是从TPersistent类的子类TComponent类继承而来的。TPersistent抽象基类具有使用流stream来存取类的属性的能力。TComponent类则是所有VCL组件的父类。这就是所

Delphi--TControl与Windows消息的封装

 

TControl是从TPersistent类的子类TComponent类继承而来的。TPersistent抽象基类具有使用流stream来存取类的属性的能力。

TComponent类则是所有VCL组件的父类。
这就是所有的VCL组件包括您的自定义组件可以使用dfm文件存取属性的原因(当然要是TPersistent的子类,我想您很少需要直接从TObject类来派生您的自定义组件吧)。

TControl类的重要性并不亚于它的父类们。在BCB的继承关系中,TControl类的是所有VCL可视化组件的父类。实际上就是控件的意思吧。所谓可视化是指您可以在运行期间看到和操纵的控件。这类控件所具有的一些基本属性和方法都在TControl类中进行定义。

TControl的实现在\Borland\CBuilder5\Source\Vcl\control.pas中可以找到。

TControl继承但并没有重写TObject的Dispatch方法。反而提供了一个新的方法WndProc。一起来看看Borland的工程师们是怎么写的吧。
[delphi]
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;  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。毋庸讳言,的确差远了。而且这个帮助还经常有问题。但有总比没有好啊。

Delphi消息的发送有三种方法:

1.Tcontrol类的Perform对象方法。可以向任何一个窗体或控件发送消息,只需要知道窗体或控件的实例。其声明如下:

function Tcontrol.Perform(Msg:Cardinal;Wparam,Lparam:Longint):Longint

2.Windows的API函数SendMessage()和Postmessage()。其声明如下:

function SendMessage(hWnd: HWND; Msg: UINT;wParam:WPARAM; lParam: LPARAM):LRESULT;stdcall;

function SendMessage(hWnd: HWND; Msg: UINT;wParam: WPARAM; lParam:LPARAM):LRESULT;stdcall

PostMessage函数将消息添加到应用程序的消息队列中去。应用程序的消息循环会从消息队列中提取登记的该消息,再发送到相应的窗口中。

TControl与Windows消息的封装

TObject提供了最基本的消息分发和处理的机制,而VCL真正对Windows系统消息的封装则是在TControl中完成的。

TControl将消息转换成VCL的事件,以将系统消息融入VCL框架中。

消息分发机制在4.2节已经介绍过,那么系统消息是如何变成事件的呢?

现在,通过观察TControl的一个代码片段来解答这个问题。在此只以鼠标消息变成鼠标事件的过程来解释,其余的消息封装基本类似。

先摘取TControl声明中的一个片段: 
 TControl = class(TComponent)

 Private

 ……
 FOnMouseDown: TMouseEvent;
 ……

 procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton;

 Shift: TShiftState);

 ……

 procedure MouseDown(Button: TMouseButton; Shift: TShiftState;

 X, Y: Integer); dynamic;

 ……

 procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;

 procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;

 procedure WMMButtonDown(var Message: TWMMButtonDown); message WM_MBUTTONDOWN;


 ……
 protected
 ……

 property OnMouseDown: TMouseEvent read FOnMouseDown write

 FOnMouseDown;

 ……

 end;
       这段代码是TControl组件类的声明。如果你从没有接触过类似的VCL组件代码的代码,不明白那些property、read、write的意思,那么可以先跳转到5.1节一下相关的基础知识,然后再回过头来到此处继续。

TControl声明了一个OnMouseDown属性,该属性读写一个称为FOnMouseDown的事件指针。因此FOnMouseDown会指向OnMouseDown事件的用户代码。

TControl声明了WMLButtonDown、WMRButtonDown、WMMButtonDown 3个消息 处理函数,它们分别处理WM_LBUTTONDOWN、WM_RBUTTONDOWN、WM _MBUTTONDOWN 3个Windows消息,对应于鼠标的左键按下、右键按下、中键按下3个硬件事件。

另外,还有一个DoMouseDown()方法和一个MouseDown()的dynamic方法,它们与消息处理函数之间2是什么样的关系呢?

现在,就来具体看一下这些函数的实现。

这里是3个消息的处理函数:

procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);
begin
 SendCancelMode(Self);
 inherited;
 if csCaptureMouse in ControlStyle then
 MouseCapture := True;
 if csClickEvents in ControlStyle then
 Include(FControlState, csClicked);
 DoMouseDown(Message, mbLeft, []);
end;

procedure TControl.WMRButtonDown(var Message: TWMRButtonDown);
begin
 inherited;
 DoMouseDown(Message, mbRight, []);
end;

procedure TControl.WMMButtonDown(var Message: TWMMButtonDown);
begin
 inherited;
 DoMouseDown(Message, mbMiddle, []);
end;

当TObject.Dispatch()将WM_LBUTTONDOWN消息、WM_RBUTTONDOWN消息或WM_MBUTTONDOWN消息分发给TControl的派生类的实例后,WMLButtonDown()、WMRButtonDown()或WMMButtonDown()被执行,然后它们都有类似这样

DoMouseDown(Message, mbRight, []); 的代码来调用DoMouseDown():

procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton; Shift: TShiftState);
begin
 if not (csNoStdEvents in ControlStyle) then
 with Message do
 if (Width > 32768) or (Height > 32768) then
 with CalcCursorPos do
 MouseDown(Button, KeysToShiftState(Keys) + Shift, X, Y)
 else
 MouseDown(Button, KeysToShiftState(Keys) + Shift, Message.XPos, Message.Ypos );
end;

在DoMouseDown()中进行一些必要的处理工作后(特殊情况下重新获取鼠标位置),就会调用MouseDown():

procedure TControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
 if Assigned(FOnMouseDown) then
 FOnMouseDown(Self, Button, Shift, X, Y);
end;

在MouseDown()中,才会通过FOnMouseDown事件指针真正去执行用户定义的OnMouseDown事件的代码。

由此,完成了Windows系统消息到VCL事件的转换过程。


因此,从TControl派生的类都可以拥有OnMouseDown事件,只不过该事件属性在TControl中被定义成protected,只有其派生类可见,并且在派生类中可以自由选择是否公布这个属性。要公布该属性只需要简单地将其声明为published即可。如:

TMyControl = class(TControl)
published
 property OnMouseDown;
end;

这些函数过程的调用关系如图4.3所示。

DoMouseDown()
MouseDown()
程序员的OnMouseDown事件代码
WMMouseDown()
Dispatch(WM_LBUTTONDOWN); Dispatch(WM_LBUTTONDOWN);


图4.3 WM_LBUTTONDOWN消息到OnMouseDown事件的转换过程

在此,只是以OnMouseDown事件为例。其实,VCL对Windows各个消息的封装大同小异,以此一例足以说明事件模型的原理。

另外,值得注意的是,在上例中的MouseDown()函数是一个dynamic方法,因此可以通过在TControl派生类中覆盖MouseDown()来处理自己所编写的鼠标按下事件,然后通过

inherited;

语句调用TControl的MouseDown()来执行使用组件的程序员所编写的OnMouseDown的代码。具体内容会在第5章中展开。
至此,读者应该已经了解了VCL事件与Windows消息的对应关系,应该知道平时为组件写的事件代码是如何被执行的


 

Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<