原来破坏一切的函数是UpdateAnchorRules。 TControl 存储 FOriginalParentSize 和它自己的原始大小在 FAnchorRules,并使用它来自动调整大小作为父调整大小。 UpdateAnchorRules() 获取当前父级大小和当前控制 Width 和 Height 并将它们保存到 FOriginalParentSize 和 FAnchorRules。
如果一切正常,在正常调整大小期间不会有任何影响,因为控件及其父级会一致地更改大小。
但是当控件Width由于锚定而小于零时,Windows,因此Delphi仍然认为它0。如果此时调用UpdateAnchorRules,它会保存错误的、不符合标准的0 原始宽度值。在此之后,布局无法修复。
(如果未调用,Width 会继续更新,因为保留了原始大小,因此与父级 Width 保持适当的关系)
结果是任何涉及创建窗口句柄调用 UpdateAnchorRules 两次:首先在 WinAPI 中 CreateWindow,因为它在返回之前调度 WM_SIZE(和 WM_SIZE 处理程序调用 UpdateAnchorRules),其次,显式在 @ 987654341@ 句柄创建后。
看来只要我们可以在CreateHandle 期间禁用UpdateAnchorRules,我们就会成功。但是在CreateHandle 中有对UpdateAnchorRules 的显式调用,这意味着有人认为需要在句柄创建后调整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;
免责声明:
我不知道在设置 csLoading 的情况下运行 TControl 操作还会破坏什么。
更好的选择是挂钩UpdateAnchorRules 过程并专门为此目的添加另一个标志检查,但这需要完全重新实现UpdateAnchorRules(容易破坏具有不同原始UpdateAnchorRules 的不同版本的Delphi)或发明某种方式来调用原始的UpdateAnchorRules,这通常会通过用钩子重写来破坏。