Delphi的重要属性,主要是Enable,  Visible, Color, left等等。这里分析left,因为TWinControl里有些覆盖函数的原因,虽然起点都是TControl.SetLeft()函数,但是图形控件和Win控件走的是不一样的路线。这里是测试TWinControl的left代码:

  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3. panel1.Left:=panel1.Left-;
  4. end;

由于left是TControl的公用属性,它的write属性是SetLeft,从这里开始追踪,这是跟踪分析代码:

  1. procedure TControl.SetLeft(Value: Integer);
  2. begin
  3. SetBounds(Value, FTop, FWidth, FHeight); // 虚函数,本类有覆盖函数
  4. Include(FScalingFlags, sfLeft);
  5. end;
  6.  
  7. procedure TWinControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
  8. var
  9. WindowPlacement: TWindowPlacement; // Windows结构类型,包含最大化最小化窗口位置等6项内容
  10. begin
  11. // 覆盖函数
  12. if (ALeft <> FLeft) or (ATop <> FTop) or (AWidth <> FWidth) or (AHeight <> FHeight) then
  13. begin
  14. if HandleAllocated and not IsIconic(FHandle) then // API
  15. // 此函数会自动触发WM_WINDOWPOSCHANGING消息和WM_WINDOWPOSCHANGED消息,见MSDN
  16. SetWindowPos(FHandle, , ALeft, ATop, AWidth, AHeight, SWP_NOZORDER + SWP_NOACTIVATE) // API
  17. end;
  18. // super 手法:前面使用API设置了真实的Windows窗口属性和Delphi控件属性后,可放心大胆的调用一些函数,
  19. // 不是TControl已经提供了通用逻辑,就是它自己定义了一些特殊的函数,可随便使用,直接产生效果,而不再依赖别人来完成某件事情。
  20. UpdateAnchorRules; // TControl类函数,通用函数
  21. RequestAlign; // TControl类函数,通用函数
  22. end;
  23. end;

碰到SetWindowPos函数时,执行前,系统向对应句柄的窗口分别发送WM_WINDOWPOSCHANGING消息,事后发送WM_WINDOWPOSCHANGED,可以要充分利用这两个消息达到目的。首先执行WM_WINDOWPOSCHANGING消息函数:

  1. procedure TWinControl.WMWindowPosChanging(var Message: TWMWindowPosChanging);
  2. begin
  3. // important 在API改变窗口大小之前,通过这个消息给予程序员准确控制的机会
  4. // TControl没有这个函数
  5. // 所有Win控件都可使用这段代码,只有Form覆盖了这个函数(也仅仅是三明治手法)
  6. // wParam用不上,lParam传递结构
  7. if ComponentState * [csReading, csDestroying] = [] then
  8. with Message.WindowPos^ do // 消息内含的指针,包含7项内容
  9. if (flags and SWP_NOSIZE = ) // 如果没有SWP_NOSIZE选项。SWP_NOSIZE = 保持当前大小,忽略cx cy
  10. and not CheckNewSize(cx, cy) // 类函数,检测并给予程序员一系列的机会与原来的属性值一起控制cx和cy的机会。但也有可能不允许改变cx和cy
  11. then
  12. flags := flags or SWP_NOSIZE; // 如果不允许改变窗口大小,就加上标志位
  13. // 再多给一次机会
  14. inherited; // fixme 好像没用
  15. end;

然后真正执行SetWindowPos API函数,真正调整了窗口的位置。最后再执行WM_WINDOWCHANGED消息函数:

  1. procedure TWinControl.WMWindowPosChanged(var Message: TWMWindowPosChanged);
  2. var
  3. Framed, Moved, Sized: Boolean;
  4. begin
  5. // 三明治手法,这里使边框失效
  6. // 判断是否有边框,是否移动了,是否改变了尺寸
  7. Framed := FCtl3D and (csFramed in ControlStyle) and (Parent <> nil) and (Message.WindowPos^.flags and SWP_NOREDRAW = );
  8. Moved := (Message.WindowPos^.flags and SWP_NOMOVE = ) and IsWindowVisible(FHandle); // API
  9. Sized := (Message.WindowPos^.flags and SWP_NOSIZE = ) and IsWindowVisible(FHandle);
  10. // 如果有边框,并且已经移动或者改变了尺寸,那么使边框无效
  11. if Framed and (Moved or Sized) then InvalidateFrame; // 类函数 fixme 这不是重复了吗?
  12. // 仅仅调整边框不够,更主要是调整控件自己的位置
  13. if not (csDestroyingHandle in ControlState) then UpdateBounds; // 类函数,使用API调整控件在屏幕上的位置
  14.  
  15. inherited; // super 三明治手法,调用程序员潜在的消息函数,并重新计算最大化最小化的限制和坞里的尺寸
  16.  
  17. // fixme 根据消息的内容,再次使边框无效(如果有显示或隐藏标记的话)
  18. if Framed and ((Moved or Sized) or (Message.WindowPos^.flags and (SWP_SHOWWINDOW or SWP_HIDEWINDOW) <> )) then
  19. InvalidateFrame; // 类函数,简单调用API
  20. end;

特别注意,TControl没有WM_WINDOWPOSCHANGING的消息函数,所以它是通过WM_WINDOWPOSCHANGED消息函数来移动eft坐标的。这里为了加强印象,不妨对比一下:

  1. procedure TControl.WMWindowPosChanged(var Message: TWMWindowPosChanged);
  2. begin
  3. // 先执行潜在的程序员消息函数
  4. // super 这里其实不会是TControl自己调用inherited,因为没有TControl的直接实例,而是由它的子类比如TLabel来调用,
  5. // 因此会调用TLabel的WM_WINDOWPOSCHANGED消息函数,如果它有的话
  6. inherited;
  7. // 后根据新的长宽,给控件最大长度和最大宽度重新赋值
  8. { Update min/max width/height to actual extents control will allow }
  9. if ComponentState * [csReading, csLoading] = [] then
  10. begin
  11. with Constraints do // 类属性
  12. begin
  13. if (MaxWidth > ) and (Width > MaxWidth) then
  14. FMaxWidth := Width
  15. else if (MinWidth > ) and (Width < MinWidth) then
  16. FMinWidth := Width;
  17. if (MaxHeight > ) and (Height > MaxHeight) then
  18. FMaxHeight := Height
  19. else if (MinHeight > ) and (Height < MinHeight) then
  20. FMinHeight := Height;
  21. end;
  22. // 根据消息传来的结构体的值,计算坞尺寸
  23. if Message.WindowPos <> nil then
  24. with Message.WindowPos^ do
  25. if (FHostDockSite <> nil) and not (csDocking in ControlState) and
  26. (Flags and SWP_NOSIZE = ) and (cx <> ) and (cy <> ) then
  27. CalcDockSizes; // 类函数
  28. end;
  29. end;

可以发现,TWinControl使用了三明治手法,增加了对边框的处理。

-------------------------------------------------------------------------------

VCL根据以上功能,提供了OnSize事件。但是各自控件是否发布这个事件,就要看它自己了。事实上,只有少数几个控件,比如TPanel,TScrollBox,TCustomForm,TControlBar一共4个类发布了这个事件。

  1. FOnResize: TNotifyEvent;
  2. property OnResize: TNotifyEvent read FOnResize write FOnResize;
  3.  
  4. procedure TControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
  5. begin
  6. // 虚函数,TWinControl有覆盖函数
  7. if CheckNewSize(AWidth, AHeight) and // TControl的类函数,重新计算长宽
  8. ((ALeft <> FLeft) or (ATop <> FTop) or (AWidth <> FWidth) or (AHeight <> FHeight)) then // 有一个不等就要重新计算和排列
  9. begin
  10. InvalidateControl(Visible, False); // TControl的类函数,非虚函数,第二个参数表示暂时设置当前控件是透明的。fixme 好像重复了,且也不明白什么意思
  11. FLeft := ALeft;
  12. FTop := ATop;
  13. FWidth := AWidth;
  14. FHeight := AHeight;
  15. UpdateAnchorRules; // TControl的类函数,坐标和长宽设置完了,就要重新铆接一下
  16. Invalidate; // TControl的类函数,调用TControl.InvalidateControl,再调用API声明无效区域
  17.  
  18. Perform(WM_WINDOWPOSCHANGED, , ); // 就这一处使用。它比WM_SIZE和WM_MOVE更高效。
  19. RequestAlign; // TControl的虚函数,各WinControl的子类可自己改写,比如TCustomForm就改写了,但Win控件没有改写
  20. if not (csLoading in ComponentState) then Resize; // 这里调用!TControl的虚函数,简单调用程序员事件。子类一般不需要改写它。
  21. end;
  22. end;
  23.  
  24. procedure TWinControl.WMSize(var Message: TWMSize);
  25. begin
  26. UpdateBounds; // 类函数
  27. inherited;
  28. Realign; // 类函数
  29. if not (csLoading in ComponentState) then Resize;
  30. end;
  31.  
  32. procedure TControl.Resize;
  33. begin
  34. if Assigned(FOnResize) then FOnResize(Self);
  35. end;

FOnCanResize: TCanResizeEvent; 和 FOnConstrainedResize: TConstrainedResizeEvent; 这两个事件也是类似。

一句话改变TWinControl控件的left坐标的前世今生(入口函数是SetBounds,然后调用SetWindowPos起作用,并发消息更新Delphi的left属性值)的更多相关文章

  1. 一句话改变TGraphicControl控件的left坐标的前世今生

    稍微用脑子想了一下,图形控件没有句柄,因此先把自己的坐标改一改,然后只要把父控件的某些区域Invalidate一下就可以了,WM_PAINT消息一来,父控件就会重绘所有子图形控件,就达到了相应的效果. ...

  2. 【MFC】mfc控件位置调整和坐标确定 .

    摘自DoubleLi:   http://www.cnblogs.com/lidabo/archive/2012/08/24/2654678.html mfc控件位置调整和坐标确定 http://my ...

  3. C#根据句柄改变窗体控件值

    需求是这样,有个程序界面我们需要通过自己的程序持续输入数据,界面如图. 可以获得控件的句柄而用钩子写入值.这里需要用到spy++工具.在VS的工具下有个spy++工具,打开如下图 通过这个工具可以获得 ...

  4. WPF通过EventTrigger改变其他控件的值

    场景:点击TextBox后弹出Poppup 原理:使用EventTrigger后触发StoryBoard,通过StoryBoard改变其他控件的值. 参考代码: <Grid> <Gr ...

  5. 将四个按钮放入一个父控件的好处:方便移动,只需要改变父控件的y值,就可移动四个按钮

      将四个按钮放入一个父控件的好处:方便移动,只需要改变父控件的y值, 就可移动四个按钮               https://www.evernote.com/shard/s227/sh/78 ...

  6. JQuery 点击控件获取当前坐标时不兼容IE7

    现在要求在点击文本框时,获取文本框的坐标,需要相对文本框的位置来显示信息. 思路就是,绑定文本框的click 事件,一旦有点击就触发,去调用clickevent 函数执行计算. $('#txt_m') ...

  7. Qt获取控件位置,坐标总结

    总结的结果是: QMouseEvent中两类坐标系统,一类是窗口坐标,一类是显示器坐标.   总结一:经过试验,QMouseEvent::globalPos()  和 QCursor::pos()效果 ...

  8. winform 勾选可以改变框控件

    public partial class UCCheck : UserControl { [Browsable(true), Category("修改属性"), Descripti ...

  9. 动态改变Android控件大小

    Button button = (Button) findViewById(R.id.button2);button.setOnClickListener(myOnClickListener); // ...

随机推荐

  1. lua字符匹配

    匹配下列格式的数据中的 source和MAC地址: Chain WiFiDog_br-lan_Outgoing (1 references) pkts bytes target prot opt in ...

  2. 模拟dispatch_once

    dispatch_once   dispatch_once可以保证一段代码只被执行一次,因此出现之后使用最多的场景就是实现单例.本文来模拟实现dispatch_once的功能. 模拟dispatch_ ...

  3. error: device not found - waiting for device -

    执行 cocos run -p android 时报的这个错误 连接上 android 手机, 手机开启开发者模式.  设置--其他高级设置--开发者选项--USB 调试

  4. PhoneGap 在eclipse上开发Android程序

    本文将记录在Eclipes上开发Android App,在使用的方法是Hybrid App(混合模式移动应用), 由于本人的工作需要,将要开发在车间使用的数据录入程序,但是其中有非常多的逻辑验证和判断 ...

  5. Java_Activiti5_菜鸟也来学Activiti5工作流_之初识常用服务类和数据表(二)

    /** * 代码清单中使用 ProcessEngines类加载默认的流程配置文件(activiti.cfg.xml),再获取各个服务组件的实例. * RepositoryService主要用于管理流程 ...

  6. CSS3条件判断——@supports/window.CSS.supports()(转)

    CSS3条件判断,听起来"不明觉厉",如果你对CSS稍为熟悉一点的话,你会发现CSS中的"@media"就是条件判断之一.是的,在CSS3的条件判断规范文档中包 ...

  7. angular调用WCF服务,读取文件夹下图片显示列表,下载另存为图片

    读取文件夹下的文件 public string ReadImagesPaths() { string result = string.Empty; try { string path = System ...

  8. asp.net 在线人数统计\页面访问量

    1.新建网站,添加几个窗体.webForm1.aspx ,ViewStateForm.aspx 2.在网站的根目录下添加全局应用程序类“Global.aspx” .(重要) 3.在“Global.as ...

  9. Bridge 模式

    Bridge 模式将抽象和行为划分开来,各自独立,但能动态的结合.在面向对象设计的基本概念中,对象这个概念实际是由属性和行为两个部分组成的,属性我们可以认为是一种静止的,是一种抽象,一般情况下,行为是 ...

  10. 【BZOJ2281】【博弈论+DP】 [Sdoi2011]黑白棋

    Description 黑白棋(game) [问题描述] 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是 ...