重建窗口时锚点被破坏的解决方法?

时间:2013-02-25 08:08:25

标签: delphi

这种情况发生在直到XE3的所有Delphi中:

  1. 创建表单并在其上放置一个面板。将面板固定到[akLeft, akTop, akRight, akBottom],但在它和边框之间留出空间。
  2. 添加一个调用RecreateWnd()
  3. 的按钮
  4. 运行该应用。调整窗体大小以隐藏面板,因为它由于锚定而小于0像素。 按下重新创建按钮。
  5. 重新调整表单大小并注意面板的锚定已损坏。
  6. 只要我记得自己使用Delphi,就会因此无法使用锚点。调整表单大小,然后停靠它:重新创建窗口,您的布局被破坏。

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

    更新

    评论中提供了两种解决方法,一种是经证实且稳定但形式眨眼,另一种是实验性但可能更彻底和清洁。

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

2 个答案:

答案 0 :(得分:4)

我使用的两个选项对于底部和右侧锚点的问题都不是非常理想的:

  1. 在调用或导致调用RecreateWnd();之前再次使窗口变大,然后再将其缩小。然而,在你再次变小之前必须可见。
  2. 设置Form的约束,使其不能重新调整大小以至于最终隐藏了内容。
  3. 闪烁较大表单的示例,使用高度和宽度足够大的值,以便不隐藏面板:

    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;
    

答案 1 :(得分:0)

事实证明,破坏一切的功能是UpdateAnchorRulesTControl FOriginalParentSize存储FAnchorRules,并且它在UpdateAnchorRules()中拥有自己的原始大小,并在父级调整大小时使用它自动调整大小。 Width获取当前父级和当前控件HeightFOriginalParentSize,并将其保存到FAnchorRulesWidth

如果一切正常,那么在正常调整大小时没有效果,因为控件和它的父级会改变大小。

但是当控件0由于锚定而小于零时,Windows,因此Delphi仍然认为它是UpdateAnchorRules。如果此时调用0,则会为原始宽度保存错误的,不一致的Width值。在此之后,布局无法修复。

(如果未调用Width,由于保留了原始尺寸,UpdateAnchorRules会继续以与父CreateWindow的正确关系进行更新。

关闭创建窗口句柄调用WM_SIZE两次的任何事情:首先在WinAPI WM_SIZE中,因为它在返回之前调度UpdateAnchorRules(和CreateHandle处理程序调用{​​{1 }}),第二,在句柄创建后显式在UpdateAnchorRules中。

似乎只要我们可以在CreateHandle期间停用UpdateAnchorRules,我们就会成功。但是CreateHandle中有UpdateAnchorRules的明确调用,这意味着有人认为需要是在创建句柄后调整Anchor规则。

所以也许我错过了什么,通过禁用它会有什么破坏?

无论如何,有两种方法可以禁用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 set运行UpdateAnchorRules操作会破坏其他什么。

更好的替代方法是挂钩UpdateAnchorRules过程并专门为此目的添加另一个标志检查,但这需要完全重新实现UpdateAnchorRules(容易在不同版本的Delphi上打破不同的原始版本UpdateAnchorRules)或发明一些方法来调用原始的{{1}},这通常是通过用钩子重写它来销毁的。