5

这发生在所有 Delphi 到 XE3 中:

  1. 创建一个表单并在其上放置一个面板。将面板锚定到[akLeft, akTop, akRight, akBottom],但在它和边框之间留出空间。
  2. 添加一个调用的按钮RecreateWnd()
  3. 运行应用程序。调整窗体的大小以隐藏面板,因为由于锚定,它的大小小于 0 像素。按 RecreateWnd 按钮。
  4. 重新调整表单大小并注意面板的锚定已损坏。

只要我记得自己使用 Delphi,锚总是因为这个而无法使用。调整表单的大小,然后将其停靠:重新创建窗口,您的布局被破坏。

我想知道是否有某种解决方法?

更新

评论中提供了两种解决方法,一种经过验证且稳定,但表单闪烁,另一种是实验性的,但可能更彻底和干净。

我暂时不会投票给任何一个,因为其中一个是我的,我什至不确定它是否稳定。相反,我会等待一些公众意见。

4

2 回答 2

4

我使用的两个选项都不是解决底部和右侧锚点问题的理想选择:

  1. 在调用或导致被调用之前将窗口再次变大 RecreateWnd();,然后再将其变小。然而,在你再次变小之前必须是可见的。
  2. 设置表单的约束,使其不能被重新调整到小到最终隐藏的东西。

闪烁较大形式的示例,使用足够大的高度和宽度值以使面板不隐藏:

procedure TForm1.Button1Click(Sender: TObject);
Var
  OldWidth, OldHeight : integer;
begin
  OldWidth := Form1.Width;
  OldHeight := Form1.Height;
  Form1.Visible := false;
  Form1.Width := 1000;
  Form1.Height := 800;
  RecreateWnd();
  Form1.Visible := true;
  Form1.Width := OldWidth;
  Form1.Height := OldHeight;
end;
于 2013-02-25T09:36:06.017 回答
0

原来破坏一切的功能是UpdateAnchorRules. TControl存储FOriginalParentSize和它自己的原始大小FAnchorRules,并使用它来自动调整大小作为父调整大小。UpdateAnchorRules()获取当前父级大小和当前控件WidthHeight并将它们保存到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通常被破坏的原始版本用钩子重写它。

于 2013-03-04T13:36:32.487 回答