原来破坏一切的功能是UpdateAnchorRules
. TControl
存储FOriginalParentSize
和它自己的原始大小FAnchorRules
,并使用它来自动调整大小作为父调整大小。UpdateAnchorRules()
获取当前父级大小和当前控件Width
,Height
并将它们保存到FOriginalParentSize
andFAnchorRules
中。
如果一切正常,那么在正常调整大小期间不会产生任何影响,因为控件及其父级会一致地更改大小。
但是当Width
由于锚定控制小于零时,Windows,因此Delphi仍然认为它0
。如果UpdateAnchorRules
在该点调用,它会保存错误的、不符合0
原始宽度的值。在此之后,布局无法修复。
(如果它没有被调用,由于保留了原始大小,Width
继续以与父级的适当关系进行更新)Width
结果是任何涉及UpdateAnchorRules
两次创建窗口句柄调用的事情:首先是在 WinAPI中,因为它在返回(和处理程序调用)之前CreateWindow
分派,其次是在句柄创建之后显式地。WM_SIZE
WM_SIZE
UpdateAnchorRules
CreateHandle
看来,只要我们能UpdateAnchorRules
在 的时间内禁用CreateHandle
,我们就会成功。但是有明确的UpdateAnchorRules
in调用CreateHandle
,这意味着有人认为在句柄创建后需要调整 Anchor 规则。
所以也许我错过了一些东西,并且通过禁用它会破坏一些东西?
无论如何,有两种现成的禁用方法UpdateAnchorRules
:设置FAnchorMove
或设置csLoading
。第一个不好,因为有代码在中途清除它RecreateWnd
然后UpdateAnchorRules
再次调用。
第二个有效,这是一个解决方案:
type
TComponentHack = class helper for TComponent
public
procedure SetCsLoading(Value: boolean);
end;
procedure TComponentHack.SetCsLoading(Value: boolean);
var i: integer;
begin
if Value then
Self.FComponentState := Self.FComponentState + [csLoading]
else
Self.FComponentState := Self.FComponentState - [csLoading];
for i := 0 to Self.ComponentCount-1 do
if Self.Components[i] is TControl then
TControl(Self.Components[i]).SetCsLoading(Value);
end;
procedure SafeRecreateWnd();
begin
MyControl.SetCsLoading(true);
try
MyControl.RecreateWnd(); //or any operation which triggers it -- such as docking or making the window visible first time after RecreateWnd()
finally
MyControl.SetCsLoading(false);
end;
end;
免责声明:
TControl
我不知道通过设置 csLoading运行操作还会破坏什么。
更好的选择是挂钩UpdateAnchorRules
过程并专门为此目的添加另一个标志检查,但这需要完全重新实现UpdateAnchorRules
(容易破坏具有不同原始版本的不同版本的Delphi UpdateAnchorRules
)或发明某种方式来调用UpdateAnchorRules
通常被破坏的原始版本用钩子重写它。