【问题标题】:Issue with controls alignment in dynamically created Frame动态创建的框架中的控件对齐问题
【发布时间】:2017-06-14 01:56:07
【问题描述】:

我的程序使用动态创建的框架,有时我会遇到控件对齐不正确的问题。

我使用自己继承自TPanel的容器控件,但使用GridPanel时也会发现同样的问题。

这里是重现问题的test 程序的源代码(带有已编译的 exe)。

关键码sn-ps:

在主窗体中:

//creating Frame from the main form
procedure TForm1.FormCreate(Sender: TObject);
begin
  f := TFrame2.Create(Self);
  f.Parent := Self;
end;

在框架中:

//constructor of the Frame
constructor TFrame2.Create(AOwner: TComponent);
begin
  inherited;
  Edit1.Clear;// the problem appears
end;

框架及其所有控件已对齐,并且必须具有主窗体的宽度,但 Edit1ComboBox1 在您手动调整窗体大小之前不会在视觉上对齐(发送 WM_SIZE 无效)。

但是,如果您评论 Edit1.Clear 行,从程序开始一切都会正常工作。此代码并非特定于错误,您可以在此处输入,例如ComboBox1.Items.Add('')

如果框架是静态创建的或将GridPanel 更改为Panel,问题就会消失。

感谢@quasoft,我制作了一个新的test2 版本,它工作得更好 - 现在控件水平对齐正确但垂直组合框不在正确的位置,可以通过更改表单大小看到。

【问题讨论】:

  • 如果您只是将Edit1.Clear 替换为Edit1.Text := '',它会按预期工作
  • 我看到你的项目,一切都很好,正如@quasoft 所说,只需替换 Edit1.Clear;
  • “如果框架是静态创建的” 在两种情况下(静态和动态)检查框架的WindowHandle 中的TFrame2.Create。您可能希望在 Frame CreateWnd 处理程序中初始化“依赖于句柄”的控件,您知道这些控件具有有效的窗口句柄。 (我现在无法测试)。
  • 是的,静态创建的框架没有对齐问题。对于动态创建的帧,我使用 HandleAllocated 和 HandleNeeded 函数来确保句柄有效(例如,在发送消息时)。这还不够吗?

标签: delphi alignment frame


【解决方案1】:

快速修复:

您的问题的快速解决方案是使用TEditText 属性,而不是Clear 方法 - 如前所述,将Edit1.Clear 替换为Edit1.Text := ''

了解问题

但是如果你打算长期在 Delphi 中使用框架,你需要更好地理解这个问题,否则它们会在你睡觉时困扰你(开玩笑)。

真正的问题是您在分配Parent 之前修改了框架的状态。

procedure TForm1.FormCreate(Sender: TObject);
begin
  f := TFrame2.Create(Self);    // <--- Your text edit is cleared inside
  f.Parent := Self;             // <--- Your frame is attached to the form here
end;

这样做不允许TGridPanel 组件在计算其列和行的大小时考虑父级的宽度、高度和位置。

使用Text属性有效,因为属性设置器不会直接更改控件的文本,而是向消息队列发送一条用于目的的消息:

Controls.pas 除外:

...
procedure TControl.SetTextBuf(Buffer: PChar);
begin
  Perform(WM_SETTEXT, 0, Longint(Buffer));
  Perform(CM_TEXTCHANGED, 0, 0);
end;

procedure TControl.SetText(const Value: TCaption);
begin
  if GetText <> Value then SetTextBuf(PChar(Value));
end;
...

这实际上会在您分配框架的Parent 后导致文本实际更改 - 因为消息队列将在表单创建方法完成后进行一点处理。

Clear 方法则直接改变文本:

StdCtrls.pas 节选:

...
procedure TCustomEdit.Clear;
begin
  SetWindowText(Handle, '');
end;
...

更好的解决方案

正如您已经了解到的,快速修复仅适用于您提供的特定示例。

更好的解决方案是在您的框架中创建一个Init 方法,并在 分配Parent 之后从主窗体调用此方法:

你的框架:

procedure TFrame2.Init;
begin
  Edit1.Clear;
  ComboBox1.Items.Add('Foo Bar');
end;

你的主要形式:

procedure TForm1.FormCreate(Sender: TObject);
begin
  f := TFrame2.Create(Self);
  f.Parent := Self;             // <--- Your frame is attached to the form here
  f.Init;                       // <--- Calls initialization code of frame
end;

【讨论】:

  • 谢谢,我测试了你提供的方法,对于这个综合测试它工作正常,对于更复杂的情况它工作得更好,但并不完美。忽略如何清除编辑文本或向组合框添加项目会影响其位置、为什么覆盖 SetParent 不起作用或为什么将 WM_SIZE 发送到正确的句柄没有效果的问题,我可以说有些问题仍未解决,例如何时对齐的控件是在动态创建的框架中动态创建的。
  • @Molochnik - 期待一个未提出的问题的解决方案是不现实的。我建议你用相应的测试用例提出一个新问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 2019-02-11
  • 2014-01-28
  • 1970-01-01
  • 2021-09-14
相关资源
最近更新 更多