深入VCL理解BCB的消息机制2

行风1112

行风1112

2016-02-19 15:33

最近很多朋友喜欢上设计,但是大家却不知道如何去做,别担心有图老师给你解答,史上最全最棒的详细解说让你一看就懂。

  重载TControl的WndProc方法

  还是先谈谈VCL的继承策略。VCL中的继承链的顶部是TObject基类。一切的VCL组件和对象都继承自TObject。

  打开BCB帮助查看TControl的继承关系:

  TObject-TPersistent-TComponent-TControl

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

  TComponent类则是所有VCL组件的父类。

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

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/bianchengyuyan/)

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

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/bianchengyuyan/)

  TControl的实现在BorlandCBuilder5SourceVclcontrol.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;

  看完这段代码,你会发现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方法。源码如下:

  

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;

  鼠标消息是由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;

展开更多 50%)
分享

猜你喜欢

深入VCL理解BCB的消息机制2

编程语言 网络编程
深入VCL理解BCB的消息机制2

深入VCL理解BCB的消息机制3

编程语言 网络编程
深入VCL理解BCB的消息机制3

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

深入VCL理解BCB的消息机制1

编程语言 网络编程
深入VCL理解BCB的消息机制1

深入理解Delphi的消息机制

Delphi
深入理解Delphi的消息机制

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

理解Windows消息机制

编程语言 网络编程
理解Windows消息机制

在BCB中使用VCL控件数组2

编程语言 网络编程
在BCB中使用VCL控件数组2

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

dwr中文例子讲解--前言

dwr中文例子讲解--前言

莪怺逺嬡沵 - QQ情侣分组

莪怺逺嬡沵 - QQ情侣分组
下拉加载更多内容 ↓