VCL中消息处理初探

Delphi–TControl与Windows音讯的包裹

TObject是基类,所以大家先看一下TObject的DISPATCH方法。Dispatch遵照传入的message来查找对应的音信管理措施,假如找不到的话,就持续开采进取到父类的新闻处理方法表中找出响应的管理办法,向来到找到截至,倘若找到顶还不曾,则调用DefaultHandle来管理该音讯。message能够是任何的连串,Dispatch借使message的头两位是音讯的ID,它正是基于ID来寻找音信管理措施的。纵然别的项目标message都得以被选取,不过TObject的子类依旧希望传入的message参数是TMessage的笔录类型或其余验证的笔录类型。
以下申明和注释摘自与system.pas:
{ TObject.Dispatch accepts any data type as its Message parameter. 
The
  first 2 bytes of the data are taken as the message id to search for
  in the objects message methods.  TDispatchMessage is an example of
  such a structure with a word field for the message id.
}
  TDispatchMessage = record
    MsgID: Word;
  end;
类的后续关系如下:
TObject->TPersistent->TComponent->TControl
TControl是因而可视化组件的父类,TControl提供了三个新的点子,WndProc:
procedure TControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
  KeyState: TKeyboardState; 
  WheelMsg: TCMMouseWheel;
begin
  //假设处在设计期
  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;

 

  //窗体可以为其颇有的组件来拍卖键盘消息。
  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

TControl是从TPersistent类的子类TComponent类世袭而来的。TPersistent抽象基类具备应用流stream来存取类的属性的力量。

  //关于鼠标的新闻
  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);//倘诺是鼠标移动的新闻,则产出hint窗口
      WM_LBUTTONDOWN,
WM_LBUTTONDBLCLK://若是是左键被按下,大概双击,假使是机关拖动方式,则起首拖动,并将左键按下的情事投入组件的情事。
        begin
          if FDragMode = dmAutomatic then
          begin
            BeginAutoDrag;
            Exit;
          end;
          Include(FControlState, csLButtonDown);
        end;
      WM_LBUTTONUP:
        Exclude(FControlState, csLButtonDown);
//假如是左键放手,则将左键按下的情景剔除。
    else
      with Mouse do
        if WheelPresent and (RegWheelMessage <> 0) and 
//假使鼠标有滚轮,何况滚轮滑动时发生了音信
          (Message.Msg = RegWheelMessage) then
        begin
          GetKeyboardState(KeyState); 
//将256虚构键的情事拷贝到缓存中去
          with WheelMsg do //填充记录
          begin
            Msg := Message.Msg;
            ShiftState := KeyboardStateToShiftState(KeyState);
            WheelDelta := Message.WParam;
            Pos := TSmallPoint(Message.LParam);
          end;
          MouseWheelHandler(TMessage(WheelMsg)); //派发鼠标滚轮的音信
          Exit;
        end;
    end;
  end
  else if Message.Msg = CM_VISIBLECHANGED then
    with Message do
      SendDockNotification(Msg, WParam, LParam);  //管理自定义新闻
  Dispatch(Message); //派发未管理的消息
end;
不过独有TWinControl可以获得核心:
procedure TWinControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
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); //调用父类的拍卖方法
      
//假若窗体被挡住並且在钦命的点没有控件,则赶回结果为在client区。
        if (Message.Result = HTTRANSPARENT) and
(ControlAtPos(ScreenToClient(
          SmallPointToPoint(TWMNCHitTest(Message).Pos)), False) <>
nil) then
          Message.Result := HTCLIENT;
        Exit;
      end;
    WM_MOUSEFIRST..WM_MOUSELAST:
      if IsControlMouseMsg(TWMMouse(Message)) then 
//鼠标音讯是还是不是直接发往组件的窗体子组件
      begin
        { Check HandleAllocated because IsControlMouseMsg might have
freed the
          window if user code executed something like Parent := nil. }
永利集团304com,        if (Message.Result = 0) and HandleAllocated 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);
  end;
  inherited WndProc(Message);
end;

TComponent类则是全体VCL组件的父类。
那正是负有的VCL组件满含你的自定义组件能够动用dfm文件存取属性的案由(当然假使TPersistent的子类,笔者想你超级少须求直接从TObject类来派生您的自定义组件吧卡塔 尔(英语:State of Qatar)。

TControl类的根本并不亚于它的父类们。在BCB的后续关系中,TControl类的是具备VCL可视化组件的父类。实际上正是控件的意思啊。所谓可视化是指你可以在运行时期见到和调节的控件。那类控件所具备的片段主干质量和章程都在TControl类中举行定义。

TControl的实今后BorlandCBuilder5SourceVclcontrol.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;

发表评论

电子邮件地址不会被公开。 必填项已用*标注