TWinControl.SetBounds与TWinControl.UpdateBounds赏析(定义和调用)
先看它们的函数内容:
- procedure TControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
- begin
- // 虚函数,TWinControl有覆盖函数
- if CheckNewSize(AWidth, AHeight) and // TControl的类函数,重新计算长宽
- ((ALeft <> FLeft) or (ATop <> FTop) or (AWidth <> FWidth) or (AHeight <> FHeight)) then // 有一个不等就要重新计算和排列
- begin
- InvalidateControl(Visible, False); // TControl的类函数,非虚函数,第二个参数表示暂时设置当前控件是透明的。fixme 好像重复了,且也不明白什么意思
- FLeft := ALeft;
- FTop := ATop;
- FWidth := AWidth;
- FHeight := AHeight;
- UpdateAnchorRules; // TControl的类函数,坐标和长宽设置完了,就要重新铆接一下
- // important 手法:属性设置完了,如果有API可以使之起作用就当场调用(关于显示部分,不需要句柄就有API使用,这是特殊情况)
- Invalidate; // TControl的类函数,调用TControl.InvalidateControl,再调用API声明无效区域
- // important 图像控件只需要使用WM_WINDOWPOSCHANGED消息即可
- // 此消息在TControl和TWinControl里都有相应的函数,图形控件使用消息再做一些自己力所能及的变化,Win控件使用消息调用类函数使之调用API真正起作用
- // 前者重新计算最大化最小化的限制和坞里的尺寸,后者使用API调整边框和控件自己的位置,当然也得重新计算最大化最小化的限制和坞里的尺寸(三明治手法)
- // 当尺寸,位置或Z轴被改变了,那么就会发送这个消息。比如调用SetWindowPos API就会发送这个消息。
- Perform(WM_WINDOWPOSCHANGED, , ); // 就这一处使用。它比WM_SIZE和WM_MOVE更高效。
- // Windows位置调整完了,还要重新对齐(本质是调用TWinControl.RequestAlign,然后调用API重新排列)
- // 但实际上是靠父Win控件重新排列自己,因为它自己没有能力拥有别的控件,当然也就不能实质上让所有控件对齐。
- RequestAlign; // TControl的虚函数,各WinControl的子类可自己改写,比如TCustomForm就改写了,但Win控件没有改写
- if not (csLoading in ComponentState) then Resize; // TControl的虚函数,简单调用程序员事件。子类一般不需要改写它。
- end;
- end;
- procedure TWinControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
- var
- WindowPlacement: TWindowPlacement; // Windows结构类型,包含最大化最小化窗口位置等6项内容
- begin
- // 覆盖函数
- // fixme 貌似没有调用父类同名函数,说明各有各的章法
- if (ALeft <> FLeft) or (ATop <> FTop) or (AWidth <> FWidth) or (AHeight <> FHeight) then
- begin
- // 如果有句柄,并且不是最小化状态,就使用API立刻调整位置(一般调用这里)
- if HandleAllocated and not IsIconic(FHandle) then // API
- // fixme 使用完毕后Windows自己会更改WindowPlacement里的数据,
- SetWindowPos(FHandle, , ALeft, ATop, AWidth, AHeight, SWP_NOZORDER + SWP_NOACTIVATE) // API,此函数会自动触发WM_WINDOWPOSCHANGED消息,见MSDN
- else
- // 如果还没有句柄,或者处于最小化状态下,使用API更改WindowPlacement结构的值,以备下次调用API直接显示
- begin
- // 重新设置左上角坐标以及长宽
- FLeft := ALeft; // TControl的类属性,通用属性
- FTop := ATop;
- FWidth := AWidth;
- FHeight := AHeight;
- if HandleAllocated then
- begin
- // 取得窗口的位置信息
- WindowPlacement.Length := SizeOf(WindowPlacement);
- GetWindowPlacement(FHandle, @WindowPlacement); // API
- // 更改窗口的位置信息
- WindowPlacement.rcNormalPosition := BoundsRect; // TControl的类属性,通用属性
- SetWindowPlacement(FHandle, @WindowPlacement); // API
- end;
- end;
- // super 手法:前面使用API设置了真实的Windows窗口属性和Delphi控件属性后,可放心大胆的调用一些函数,
- // 不是TControl已经提供了通用逻辑,就是它自己定义了一些特殊的函数,可随便使用,直接产生效果,而不再依赖别人来完成某件事情。
- UpdateAnchorRules; // TControl类函数,通用函数
- RequestAlign; // TControl类函数,通用函数
- end;
- end;
- procedure TWinControl.UpdateBounds;
- var
- ParentHandle: HWnd;
- Rect: TRect;
- WindowPlacement: TWindowPlacement;
- begin
- // 非最小化状态下,取得Win控件显示区域
- if IsIconic(FHandle) then // API
- begin
- WindowPlacement.Length := SizeOf(WindowPlacement);
- GetWindowPlacement(FHandle, @WindowPlacement); // API
- Rect := WindowPlacement.rcNormalPosition;
- end else
- GetWindowRect(FHandle, Rect); // API
- // important 如果具有子窗口风格,就要根据父控件在屏幕上的位置重新显示
- if GetWindowLong(FHandle, GWL_STYLE) and WS_CHILD <> then
- begin
- ParentHandle := GetWindowLong(FHandle, GWL_HWNDPARENT); // API,fixme 还可以这样简单取得父控件句柄?
- if ParentHandle <> then
- begin
- Windows.ScreenToClient(ParentHandle, Rect.TopLeft); // API
- Windows.ScreenToClient(ParentHandle, Rect.BottomRight);
- end;
- end;
- // important 显示完以后,根据windows内部信息更新Win控件的属性
- FLeft := Rect.Left;
- FTop := Rect.Top;
- FWidth := Rect.Right - Rect.Left;
- FHeight := Rect.Bottom - Rect.Top;
- // 更新坐标和长宽后,要重新铆接
- // fixme 难道不用对齐吗,别处好多地方也都是这样
- UpdateAnchorRules;
- end;
后看看SetBounds是怎么被调用的:
- procedure TControl.SetLeft(Value: Integer);
- begin
- SetBounds(Value, FTop, FWidth, FHeight); // 虚函数,本类有覆盖函数
- Include(FScalingFlags, sfLeft);
- end;
- procedure TControl.SetTop(Value: Integer);
- begin
- SetBounds(FLeft, Value, FWidth, FHeight);
- Include(FScalingFlags, sfTop);
- end;
- procedure TControl.SetWidth(Value: Integer);
- begin
- SetBounds(FLeft, FTop, Value, FHeight);
- Include(FScalingFlags, sfWidth);
- end;
- procedure TControl.SetHeight(Value: Integer);
- begin
- SetBounds(FLeft, FTop, FWidth, Value);
- Include(FScalingFlags, sfHeight);
- end;
- procedure TControl.SetBoundsRect(const Rect: TRect);
- begin
- with Rect do SetBounds(Left, Top, Right - Left, Bottom - Top); // 类函数
- end;
- procedure TControl.SetAlign(Value: TAlign);
- var
- OldAlign: TAlign;
- begin
- if FAlign <> Value then
- begin
- OldAlign := FAlign;
- FAlign := Value;
- Anchors := AnchorAlign[Value];
- if not (csLoading in ComponentState) and (not (csDesigning in ComponentState) or (Parent <> nil))
- and (Value <> alCustom) and (OldAlign <> alCustom) then
- if ((OldAlign in [alTop, alBottom]) = (Value in [alRight, alLeft])) and
- not (OldAlign in [alNone, alClient]) and not (Value in [alNone, alClient]) then
- SetBounds(Left, Top, Height, Width) // 类函数
- else
- AdjustSize; // 类函数
- end;
- RequestAlign; // 类函数
- end;
- procedure TControl.ChangeScale(M, D: Integer);
- var
- X, Y, W, H: Integer;
- Flags: TScalingFlags;
- begin
- if M <> D then
- begin
- if csLoading in ComponentState then
- Flags := ScalingFlags
- else
- Flags := [sfLeft, sfTop, sfWidth, sfHeight, sfFont];
- if sfLeft in Flags then
- X := MulDiv(FLeft, M, D) else // 复杂
- X := FLeft;
- if sfTop in Flags then
- Y := MulDiv(FTop, M, D) else
- Y := FTop;
- if (sfWidth in Flags) and not (csFixedWidth in ControlStyle) then
- if sfLeft in Flags then
- W := MulDiv(FLeft + FWidth, M, D) - X else
- W := MulDiv(FWidth, M, D)
- else W := FWidth;
- if (sfHeight in Flags) and not (csFixedHeight in ControlStyle) then
- if sfHeight in Flags then
- H := MulDiv(FTop + FHeight, M, D) - Y else
- H := MulDiv(FTop, M, D)
- else H := FHeight;
- SetBounds(X, Y, W, H); // 类函数
- if [sfLeft, sfWidth] * Flags <> [] then
- FOriginalParentSize.X := MulDiv(FOriginalParentSize.X, M, D);
- if [sfTop, sfHeight] * Flags <> [] then
- FOriginalParentSize.Y := MulDiv(FOriginalParentSize.Y, M, D);
- if not ParentFont and (sfFont in Flags) then
- Font.Size := MulDiv(Font.Size, M, D);
- end;
- FScalingFlags := [];
- end;
- procedure TControl.SetClientSize(Value: TPoint);
- var
- Client: TRect;
- begin
- Client := GetClientRect; // 类函数
- SetBounds(FLeft, FTop, Width - Client.Right + Value.X, Height - Client.Bottom + Value.Y); // 类函数
- end;
- procedure TControl.AdjustSize;
- begin
- // 根据类属性重新设置长宽
- if not (csLoading in ComponentState) then
- SetBounds(Left, Top, Width, Height);
- end;
- procedure TWinControl.ScaleBy(M, D: Integer);
- const
- SWP_HIDE = SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_HIDEWINDOW; // 一个隐藏,其它都一样
- SWP_SHOW = SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_SHOWWINDOW; // 一个显示
- var
- IsVisible: Boolean;
- R: TRect;
- begin
- IsVisible := HandleAllocated and IsWindowVisible(Handle); // API
- if IsVisible then SetWindowPos(Handle, , , , , , SWP_HIDE); // API
- R := BoundsRect; // 类函数
- ChangeScale(M, D); // 类函数
- SetBounds(R.Left, R.Top, Width, Height); // 类函数
- if IsVisible then SetWindowPos(Handle, , , , , , SWP_SHOW); // API
- end;
再看看UpdateBounds是如何被调用的:
- procedure TWinControl.CreateWnd;
- var
- Params: TCreateParams; // 是一个Record,即在栈上分配内存。出了这个函数,这部分内存就被收回。
- TempClass: TWndClass; // Windows标准类的记录
- ClassRegistered: Boolean;
- begin
- CreateParams(Params); // 虚函数,类函数,就这一处被调用。important7 虚函数的作用就是下降,它有可能会执行子类的同名覆盖函数去了。
- with Params do
- begin
- if (WndParent = ) and (Style and WS_CHILD <> ) then
- if (Owner <> nil) and (csReading in Owner.ComponentState) and (Owner is TWinControl) then
- WndParent := TWinControl(Owner).Handle // important 记录句柄,Params的11项内容之一。注意,这里记录的是Owner的句柄,而并非Parent的句柄
- else
- raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);
- FDefWndProc := WindowClass.lpfnWndProc; // important 就这一处。这里使用Delphi类属性记录系统默认的窗口函数,不是实例对象的窗口函数。
- ClassRegistered := GetClassInfo(WindowClass.hInstance, WinClassName, TempClass); // API,第一个参数是创造这个类的instance,第二个参数是类名(Delphi的结构体项),第三个参数是取到的类信息放在里面
- if not ClassRegistered or (TempClass.lpfnWndProc <> @InitWndProc) then
- begin
- if ClassRegistered then
- Windows.UnregisterClass(WinClassName, WindowClass.hInstance); // API
- WindowClass.lpfnWndProc := @InitWndProc; // item10.xxx = 全局函数,InitWndProc函数与标准WndProc函数的参数完全一致
- WindowClass.lpszClassName := WinClassName; // item10.xxx = item11 补充刚才CreateParams没有初始化的内容
- if Windows.RegisterClass(WindowClass) = then RaiseLastOSError; // API
- end;
- CreationControl := Self; // 全局变量,记录下来以供InitWndProc使用,而且CreationControl变量有大用处。
- CreateWindowHandle(Params); // important 注意,这可是虚函数,比如TEdit就覆盖了。
- if FHandle = then RaiseLastOSError;
- if (GetWindowLong(FHandle, GWL_STYLE) and WS_CHILD <> ) and (GetWindowLong(FHandle, GWL_ID) = ) then // API
- SetWindowLong(FHandle, GWL_ID, FHandle); // API important 设置新ID,用句柄当作ID,比较有创意 fixme 不知道干嘛用的 fixme 看看执行了几次?
- end;
- StrDispose(FText); // fixme 为什么?可能是仅仅在创建窗口时有用,等创建完以后,大多数类不需要它,比如TButton不需要它 important TControl的消息处理的就是它
- FText := nil;
- UpdateBounds; // 类函数,调用API设置上下左右
- Perform(WM_SETFONT, FFont.Handle, ); // fixme 为什么?TEdit里有例子
- if AutoSize then AdjustSize; // 类函数,调用API显示和对齐
- end;
- procedure TWinControl.WMWindowPosChanged(var Message: TWMWindowPosChanged);
- var
- Framed, Moved, Sized: Boolean;
- begin
- // 三明治手法,这里使边框失效
- // 判断是否有边框,是否移动了,是否改变了尺寸
- Framed := FCtl3D and (csFramed in ControlStyle) and (Parent <> nil) and (Message.WindowPos^.flags and SWP_NOREDRAW = );
- Moved := (Message.WindowPos^.flags and SWP_NOMOVE = ) and IsWindowVisible(FHandle); // API
- Sized := (Message.WindowPos^.flags and SWP_NOSIZE = ) and IsWindowVisible(FHandle);
- // 如果有边框,并且已经移动或者改变了尺寸,那么使边框无效
- if Framed and (Moved or Sized) then InvalidateFrame; // 类函数 fixme 这不是重复了吗?
- // 仅仅调整边框不够,更主要是调整控件自己的位置
- if not (csDestroyingHandle in ControlState) then UpdateBounds; // 类函数,使用API调整控件在屏幕上的位置
- inherited; // super 三明治手法,调用程序员潜在的消息函数,并重新计算最大化最小化的限制和坞里的尺寸
- // fixme 根据消息的内容,再次使边框无效(如果有显示或隐藏标记的话)
- if Framed and ((Moved or Sized) or (Message.WindowPos^.flags and (SWP_SHOWWINDOW or SWP_HIDEWINDOW) <> )) then
- InvalidateFrame; // 类函数,简单调用API
- end;
- procedure TWinControl.WMSize(var Message: TWMSize);
- begin
- UpdateBounds; // 类函数
- inherited;
- Realign; // 类函数
- if not (csLoading in ComponentState) then Resize;
- end;
- procedure TWinControl.WMMove(var Message: TWMMove);
- begin
- inherited;
- UpdateBounds;
- end;
感觉也很重要~
TWinControl.SetBounds与TWinControl.UpdateBounds赏析(定义和调用)的更多相关文章
- javascript 内部函数的定义及调用
内部函数:定义在另一个函数中的函数 例如: <script> function outer(){ function inner(){ } } </script> inner() ...
- O-C相关04:类方法的概述与定义和调用
类方法的概述与定义和调用 1, 类方法的概述 类方法(class method)在其他编程语言中常常称为静态方法(例如 Java 或 C# 等). 与实例方法不同的是,类方法只需要使用类名即可调用, ...
- Python基础--函数的定义和调用
一.函数的作用: 提高代码的可读性,减少代码的冗余,方便调用和修改,组织结构清晰 二.函数的定义:函数遵循先定义后调用的原则 1.无参函数 def funcname(): #def 是关键字,后跟函数 ...
- Linux Shell函数定义与调用
一.Shell函数定义格式 shell函数定义格式,各部分说明如下: [ function ]等中括号括起来部分----表示可选(即可有可无) your_function_name部分----为函数名 ...
- Python函数的定义与调用、返回值、参数
一.函数是什么 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.比如print(),len()等.但你也可以自己创建函数,这被叫做用户自 ...
- Kotlin基础(二)函数的定义与调用
函数的定义与调用 一.预备知识:在Kotlin中创建集合 fun main(args: Array<String>) { //Kotlin中定义各自集合 val ,,,) val list ...
- python 函数定义及调用
1.什么是函数? 在程序中,函数就是具备某一功能的工具,事先将工具准备好即函数的定义:遇到应用场景拿来就用即函数的调用:函数必须遵循先定义后调用的原则 2.为什么要用函数 不用函数的问题是: 程序冗长 ...
- PYTHON-函数的定义与调用,返回值,和参数
函数基础'''1. 什么是函数 具备某一功能的工具->函数 事先准备工具的过程--->函数的定义 遇到应用场景,拿来就用---->函数的调用 函数分类两大类: 1. 内置函数 2. ...
- 自己定义TextView 调用ttf格式字体
方法一:自己定义TextView 调用ttf格式字体 <strong>将ttf格式文件存放在assets/fonts/下</strong> 注:PC系统字体存放在C:\Wind ...
随机推荐
- Spark1.0.0 监测方法
Spark1.0.0能够通过下面几种方式来对Spark应用程序进行监控: Spark应用程序的WebUI或者Spark Standalone的集群监控 指标,然后通过支持指标收集的集群监控 ...
- 使用strace追踪多个进程
http://www.ttlsa.com/tools/use-strace-to-track-multiple-processes/ strace是Linux环境下的一款程序调试工具,用来监察一个应 ...
- 亲测PHpnow 安装环境
出现问题1: "C:\Windows\system32\7z.exe"' 不是 或批处理文件. 找不到 C:\Windows\system32\7z.exe------------ ...
- svn 1.8.11 命令行提交新添加文件错误
由于公司的svn服务器版本不兼容最新的svn 1.8.11导致 提交代码报错 ➜ images svn ci arrowico.png -m"add images for png ico ...
- linux reboot命令
命令简介: 该命令用来重启Linux系统.相当于Windows系统中的restart命令. 命令语法: /sbin/reboot [-n] [-w] [-d] [-f] [-i] 或 reboot [ ...
- 第八篇:python高级之多进程
python高级之多进程 python高级之多进程 本节内容 多进程概念 Process类 进程间通讯 进程同步 进程池 1.多进程概念 multiprocessing is a package ...
- Python之路【第十篇】:HTML -暂无等待更新
Python之路[第十篇]:HTML -暂无等待更新
- 快速记忆JavaScript中exec和match的使用
JS模式匹配中exec,match用得非常多,所以掌握其用法对我们进行字符串的处理帮助非常大 1.exec的定义其用法 exec与match主要的不同是,exec是正则表达式里面的方法. ...
- CakePHP的blog教程三
简单的身份验证和授权应用 接着我们blog教程的例子,如果我们想要建立一个根据登录的用户身份来决定其安全访问到正确的urls. 同时我们还有其他的需求: 允许我们的blog有多个作者,每一个作者都可以 ...
- move file create directory.
If we want to move file to the directory that does not exist,and if we perform a File.Move,it will r ...