TControl将系统消息转换成VCL事件,以将系统消息融入VCL中。鼠标的事件就是一个很好的例子,当我们单击鼠标时会产生鼠标的单击消息,而这个单击消息是怎么被处理并执行的呢?
在Control单元中有这样的一段代码:
1 TControl = class(TComponent) 2 private 3 ... 4 FControlState: TControlState; //状态 //TControlState = set of (csLButtonDown, csClicked, .... , ); 5 ... 6 FOnMouseDown: TMouseEvent; //用以接受用户的定义的方法。 7 ... 8 procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton; Shift: TShiftState); 9 ... 10 procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN; //鼠标左键 11 procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN; //鼠标右键 12 procedure WMMButtonDown(var Message: TWMMButtonDown); message WM_MBUTTONDOWN; //鼠标中间键 13 protected 14 procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic; 15 ... 16 property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown; 17 end;
TControl声明了一个OnMouseDown属性,该属性读写一个称为FOnMouseDown的事件指针。当我们需要处理一个按钮被按下的情况时,我们便会定义一个按钮按下的方法,并赋值给按钮OnMouseDown属性。此时,FOnMouseDown就会指向这个用户定义的方法。
当我们左键按下鼠标时候,TObject会调用Dispatch()方法会分发WM_LBUTTIONDOWN消息分发,然后进入相应的消息处理函数。如下:
1 procedure TControl.WMLButtonDown(var Message: TWMLButtonDown); 2 begin 3 SendCancelMode(Self); 4 inherited; 5 if csCaptureMouse in ControlStyle then MouseCapture := True; 6 if csClickEvents in ControlStyle then Include(FControlState, csClicked); 7 DoMouseDown(Message, mbLeft, []); 8 end;
如果是单击事件,则在FControlState中添加csClicked。并且跳转到DoMouseDown()方法中。
1 procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton; 2 Shift: TShiftState); 3 begin 4 if not (csNoStdEvents in ControlStyle) then 5 with Message do 6 if (Width > 32768) or (Height > 32768) then 7 with CalcCursorPos do 8 MouseDown(Button, KeysToShiftState(Keys) + Shift, X, Y) 9 else 10 MouseDown(Button, KeysToShiftState(Keys) + Shift, Message.XPos, Message.YPos); 11 end;
在DoMouseDown()中进行一些必要的处理工作后,就会调用MouseDown();
1 procedure TControl.MouseDown(Button: TMouseButton; 2 Shift: TShiftState; X, Y: Integer); 3 begin 4 if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X, Y); 5 end;
在MouseDown()中,就会通过FOnMouseDown事件指针去执行用户定义的代码。此时就完成了从一个WM_LBUTTIONDOWN消息转换成相应的代码过程。
另外,值得注意的是,鼠标的Click事件是没有相应的消息的,鼠标的单击事件被包含在WM_LBUTTIONDOWN消息中,而鼠标的双击事件的消息是WM_LBUTTONDBLCLK消息。而,我们如果要单击鼠标,会先处理OnMouseDown()事件,然后再处理OnClick()事件。并且,如果我们双击鼠标,首先先处理OnMouseDown()事件,然后再处理OnDblClick()事件。这一点很好被验证:
1 procedure TForm1.FormClick(Sender: TObject); 2 begin 3 ShowMessage(‘click‘); 4 end; 5 6 procedure TForm1.FormDblClick(Sender: TObject); 7 begin 8 ShowMessage(‘dblclick‘); 9 end; 10 11 procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; 12 Shift: TShiftState; X, Y: Integer); 13 begin 14 ShowMessage(‘Down‘); 15 end;
如果我们不注释第14行,那么无论我们如何点击鼠标,都只能处理OnMouseDown()事件,然后ShowMessage()并得到焦点,OnClick()或OnDblCLick事件就不会被执行到。同样,当我们双击鼠标的时候,首先会ShowMessage(‘click‘)。