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, 0, 0); // 就这一处使用。它比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, 0, 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 <> 0 then begin ParentHandle := GetWindowLong(FHandle, GWL_HWNDPARENT); // API,fixme 还可以这样简单取得父控件句柄? if ParentHandle <> 0 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是怎么被调用的:
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/67900.html