情景如下:
如果我在Screen.Width
属性大于1200px的监视器上运行此应用程序(我在没有任何DPI虚拟化AFAIK的情况下运行),那么TGroupBox
会按照您的预期呈现。
但是..如果显示器的宽度小于1200像素,则无论您如何调整表格大小,屏幕上都会丢失控件的右手部分。
我已使用Create()
指令覆盖了表单的override;
方法,并验证我正确设置了width
属性,但仍然会裁剪控件。
任何人都可以建议如何:
a)设置表单的width属性,使其影响子组件的位置或...
b)建议在呈现表单后强制重新布局所有子组件的方法吗?
答案 0 :(得分:3)
跟踪代码以查看发生的情况,我想出了以下调整。
procedure TForm1.WMWindowPosChanging(var Message: TWMWindowPosChanging);
var
MessageWidth: Integer;
begin
MessageWidth := Message.WindowPos.cx;
inherited;
if MessageWidth > Message.WindowPos.cx then
GroupBox1.Width := GroupBox1.Width - MessageWidth + Message.WindowPos.cx;
end;
这不是一个通用的解决方案,但它清楚地表明了问题所在。 VCL要求其窗体大小不会被操作系统授予,因为它比桌面大。从那时起,表单将恢复锚定子控件的设计时间指定宽度,该宽度大于表单的客户端宽度,因此子控件的右侧溢出。
另一种解决方案可以是覆盖WM_GETMINMAXINFO
消息的处理,让操作系统授予所请求的宽度。
procedure TForm1.WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo);
begin
inherited;
Message.MinMaxInfo.ptMaxTrackSize.X := 1200;
end;
这可能不是一个好的解决方案,因为那时表单会比桌面大。
关于你的'a'和'b'项目,我不认为'b'是可能的 - 或者至少不可能自己进行VCL重新布局 - 因为VCL推迟应用锚定规则直到组件之后(形式) )完成加载。到那时,表单的宽度与设计时间宽度不同,但子控件的放置不受影响。没有任何强制布局会使它们再次同步。
但是,如果您自己的代码保留对设计时间宽度的引用,则应该可以从头开始重新计算所有内容。以下不是完整的代码。
type
TForm1 = class(TForm)
..
private
FAdjustShrinkWidth, FAdjustShrinkHeight: Integer;
protected
procedure Loaded; override;
public
procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer;
AHeight: Integer); override;
end;
...
procedure TForm1.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
var
TrackWidth, TrackHeight: Boolean;
begin
TrackWidth := AWidth = 1200;
TrackHeight := AHeight = ??;
inherited;
if TrackWidth and (Width < AWidth) then
FAdjustShrinkWidth := AWidth - Width;
if TrackHeight and (Height < AHeight) then
FAdjustShrinkHeight := AHeight - Height;
end;
procedure TForm1.Loaded;
procedure ReadjustControlAnchors(Control: TWinControl);
var
i: Integer;
begin
for i := 0 to Control.ControlCount - 1 do
if (akRight in Control.Controls[i].Anchors) or (akBottom in Control.Controls[i].Anchors) then begin
Control.Controls[i].Left := // some complex calculation depending on the anchors set;
Control.Controls[i].Top := // same as above;
Control.Controls[i].Width := // same as above;
Control.Controls[i].Height := // same as above;
if (Control.Controls[i] is TWinControl) and (TWinControl(Control.Controls[i]).ControlCount > 0) then
ReadjustControlAnchors(TWinControl(Control.Controls[i]));
end;
end;
begin
inherited;
ReadjustControlAnchors(Self);
end;
我不知道如何填写上面代码中的空白。阅读和追踪VCL代码可能是模仿VCL锚定的必要条件。
我想不出'a'的任何事情。
<小时/> 的更新强>
实际上,VCL实际上已经留下了一个后门控制器,以便在他们正在锚定时向其直接的孩子说谎他们的父母的大小。 Documentation解释说有点不同:
UpdateControlOriginalParentSize是一个更新的受保护方法 父控件的原始大小。它在内部用于更新 控制的锚定规则。
我们可以用它来告诉groupbox预期的原始大小。
type
TForm1 = class(TForm)
..
private
FWidthChange, FHeightChange: Integer;
protected
procedure UpdateControlOriginalParentSize(AControl: TControl;
var AOriginalParentSize: TPoint); override;
public
procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer;
AHeight: Integer); override;
end;
...
procedure TForm1.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
var
RequestedWidth, RequestedHeight: Integer;
begin
RequestedWidth := AWidth;
RequestedHeight := AHeight;
inherited;
if csLoading in ComponentState then begin
if RequestedWidth <> Width then
FWidthChange := Width - AWidth;
if RequestedHeight <> Height then
FHeightChange := Height - AHeight;
end;
end;
procedure TForm1.UpdateControlOriginalParentSize(AControl: TControl;
var AOriginalParentSize: TPoint);
begin
inherited;
if akRight in AControl.Anchors then
AOriginalParentSize.X := AOriginalParentSize.X - FWidthChange;
if akBottom in AControl.Anchors then
AOriginalParentSize.Y := AOriginalParentSize.Y - FHeightChange;
end;
我再次注意到这只会影响表格的直接孩子。如果groupbox托管了向右和向下锚定的控件,它还必须覆盖相同的方法。
另请注意,这不会撤消表单宽度已更改的事实。那就是如果左边的锚定控件位于窗体的最右边,它就不会将自身替换为客户端边界。它会像表格的宽度减少一样,即保持不在视线范围内。