深入VCL领略BCB的动静机制2
当前位置:以往代写 > C/C++ 教程 >深入VCL领略BCB的动静机制2
2019-06-13

深入VCL领略BCB的动静机制2

深入VCL领略BCB的动静机制2

副标题#e#

重载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的实此刻\Borland\CBuilder5\Source\Vcl\control.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;


#p#副标题#e#

看完这段代码,你会发明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要领。源码如下:

#p#分页标题#e#

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_NCHITTEST:
    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;
    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);
  else
   with Mouse do
    if WheelPresent and (RegWheelMessage <> 0) and
     (Message.Msg = RegWheelMessage) then
    begin
     GetKeyboardState(KeyState);
     with WheelMsg do
     begin
      Msg := Message.Msg;
      ShiftState := KeyboardStateToShiftState(KeyState);
      WheelDelta := Message.WParam;
      Pos := TSmallPoint(Message.LParam);
     end;
     MouseWheelHandler(TMessage(WheelMsg));
     Exit;
    end;
  end;
  inherited WndProc(Message);
end;

#p#副标题#e#

鼠标动静是由IsControlMouseMsg要领来处理惩罚的。只有再跟到IsControlMouseMsg去看看啦。源码如下:

function TWinControl.IsControlMouseMsg(var Message: TWMMouse): Boolean;
var
  //TControl呈现啦
  Control: TControl;
  P: TPoint;
begin
  if GetCapture = Handle then
  begin
   Control := nil;
   if (CaptureControl <> nil) and (CaptureControl.Parent = Self) then
    Control := CaptureControl;
  end else
   Control := ControlAtPos(SmallPointToPoint(Message.Pos), False);
  Result := False;
  if Control <> nil then
  begin
   P.X := Message.XPos - Control.Left;
   P.Y := Message.YPos - Control.Top;
   file://TControl的Perform要领将动静交由WndProc处理惩罚。
   Message.Result := Control.Perform(Message.Msg, Message.Keys, Longint(PointToSmallPoint(P)));
   Result := True;
  end;
end;

本来如此,TWinControl最后照旧将鼠标动静交给TControl的WndProc来处理惩罚了。这里呈现的Perform要领在BCB的辅佐里可以查到是TControl类中开始呈现的要领。它的浸染就是将指定的动静通报给TControl的WndProc进程。

结论就是TControl类的WndProc要领的动静是由TwinControl类在其重载的WndProc要领中挪用IsControlMouseMsg要领后利用Peform要领通报获得的。

#p#副标题#e#

由于这个原因,BCB和Delphi中的TControl类及其所有的派生类都有一个先天的并且是必需的限制。那就是所有的TControl类及其派生类的Owner必需是TwinControl类可能TWinControl的派生类。Owner属性最早可以在TComponent中找到,一个组件可能控件是由它的Owner拥有并认真释放其内存的。这就是说,当Owner从内存中释放的时候,它所拥有的所有控件占用的内存也都被释放了。Owner最好的例子就是Form。Owner同时也认真动静的分配,当Owner吸收到动静的时候,它认真将应该通报给其所拥有的控件的动静通报给它们。这样这些控件就可以或许取得处理惩罚动静的本领。TImage就是个例子:你可以发明Borland并没有让TImage重载TControl的WndProc要领,所以TImage也只有处理惩罚鼠标动静的本领,而这种本领正是来自TControl的。

唧唧崴崴的说了一大堆。终于可以说处理惩罚动静的第二种要领就是重载TControl的WndProc要领了。例程如下:

#p#分页标题#e#

void __fastcall TForm1::WndProc(TMessage &Message)
{
    switch (Message.Msg)
    {
       case WM_CLOSE:
          OnCLOSE(Message); // 处理惩罚WM_CLOSE动静的要领
       break;
    }
    TForm::WndProc(Message);
}

乍看起来,这和上次讲的重载Dispatch要领好象差不多。但实际上照旧有不同的。不同就在先后序次上,从前面TControl的WndProc可以看到,动静是先交给WndProc来处理惩罚,最后才挪用Dispatch要领的啦。

这样,重载WndProc要领可以比重载Dispatch要领更早一点点获得动静并处理惩罚动静。

好了,这次就说到这里。在您的应用措施里尚有没有比这更早获得动静的步伐呢?有,下次再说。

    关键字:

在线提交作业