原来破坏一切的功能是UpdateAnchorRules. TControl存储FOriginalParentSize和它自己的原始大小FAnchorRules,并使用它来自动调整大小作为父调整大小。UpdateAnchorRules()获取当前父级大小和当前控件Width,Height并将它们保存到FOriginalParentSizeandFAnchorRules中。
如果一切正常,那么在正常调整大小期间不会产生任何影响,因为控件及其父级会一致地更改大小。
但是当Width由于锚定控制小于零时,Windows,因此Delphi仍然认为它0。如果UpdateAnchorRules在该点调用,它会保存错误的、不符合0原始宽度的值。在此之后,布局无法修复。
(如果它没有被调用,由于保留了原始大小,Width继续以与父级的适当关系进行更新)Width
结果是任何涉及UpdateAnchorRules两次创建窗口句柄调用的事情:首先是在 WinAPI中,因为它在返回(和处理程序调用)之前CreateWindow分派,其次是在句柄创建之后显式地。WM_SIZEWM_SIZEUpdateAnchorRulesCreateHandle
看来,只要我们能UpdateAnchorRules在 的时间内禁用CreateHandle,我们就会成功。但是有明确的UpdateAnchorRulesin调用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通常被破坏的原始版本用钩子重写它。