获取窗体的大小和边框

5

我想要将子表单与主表单“并排对齐”,但遇到一些困难。

要重现这个问题,请创建一个新的VCL应用程序,并向表单中添加一个按钮:

procedure TForm1.Button1Click(Sender: TObject);
var
  Form: TForm1;
begin
  Application.CreateForm(TForm1, Form);
  Form.BorderStyle := bsSingle;
  Form.Left := Left + Width;
  Form.Top := Top;
  Form.Show;
end;

结果如下:

Windows 7:enter image description here

Windows 10:enter image description here

使用 Winapi.DwmApi 后:

DXR1 := 0;
DXL2 := 0;
if (Win32MajorVersion >= 6) and DwmCompositionEnabled then begin
  DwmGetWindowAttribute(Handle, DWMWA_EXTENDED_FRAME_BOUNDS, @R1, SizeOf(R1));
  Winapi.Windows.GetWindowRect(Handle, R2);
  DXR1 := R2.Right - R1.Right;
  DYT1 := R2.Top   - R1.Top;
end;

FormJob.Left := Left + Width - DXR1;
FormJob.Top := Top - DYT1;
FormJob.Show;

if (Win32MajorVersion >= 6) and DwmCompositionEnabled then begin
  DwmGetWindowAttribute(FormJob.Handle, DWMWA_EXTENDED_FRAME_BOUNDS, @R1, SizeOf(R1));
  Winapi.Windows.GetWindowRect(FormJob.Handle, R2);
  DXL2 := R1.Left - R2.Left;
  DYT2 := R2.Top  - R1.Top;
end;
FormJob.Left := FormJob.Left - DXL2;
FormJob.Top := FormJob.Top + DYT2;

现在,这在 Windows 7 和 Windows 10 上完全对齐。
Windows 7: enter image description here Windows 10: enter image description here 但是要做到这一点,我需要先显示子窗体。如果在显示之前调用 DwmGetWindowAttribute 以获取子窗体(和隐藏窗体)的值,则会得到与 GetWindowRect 相同的值。在显示之前无法获得此值吗?

5
DWM有一个怪癖,即在窗口显示之前无法查询框架边界。您可以使用DWMWA_CLOAK在显示窗口之前将其隐藏。 - Jonathan Potter
我认为这个问题缺少关于为什么表单一开始没有对齐的上下文。提供一个复现案例会更好。 - Sertac Akyuz
@SertacAkyuz,但是代码的第一部分已经复制了问题,还是我需要扩展这部分? - Alex Egorov
我在一个新的VCL应用程序中添加了第二个窗体。从主窗体调用您显示的代码,将FormJob替换为Form2,并且无法复制该问题。表单对齐正常。在Windows 7上测试过。 - Sertac Akyuz
当然,我使用一个新项目创建了更新后的复现代码,但仅在Win10上提供的代码与屏幕截图中展示的效果类似。 - Alex Egorov
显示剩余3条评论
1个回答

8
感谢 Jonathan Potter 的评论,现在我有以下代码并且它可以工作:
var
  R1, R2: TRect;
  DXR1, DXL2, DYT1, DYT2: Integer;
  bCloak: BOOL; // Can't use Boolean here
begin
  Application.CreateForm(TFormJob, FormJob);
  if (Win32MajorVersion >= 6) and DwmCompositionEnabled then begin
    DXR1 := 0;
    DXL2 := 0;
    DYT1 := 0;
    DYT2 := 0;

    if (DwmGetWindowAttribute(Handle, DWMWA_EXTENDED_FRAME_BOUNDS, @R1, SizeOf(R1)) = S_OK) and
       Winapi.Windows.GetWindowRect(Handle, R2) then begin
      DXR1 := R2.Right - R1.Right; // Right width of the shadow for parent
      DYT1 := R2.Top   - R1.Top;   // Top height of the shadow for parent
    end;

    bCloak := True; // Make form invisible
    DwmSetWindowAttribute(FormJob.Handle, DWMWA_CLOAK, @bCloak, SizeOf(bCloak));
    FormJob.Show; // Draw invisible form

    if (DwmGetWindowAttribute(FormJob.Handle, DWMWA_EXTENDED_FRAME_BOUNDS, @R1, SizeOf(R1)) = S_OK) and
       Winapi.Windows.GetWindowRect(FormJob.Handle, R2) then begin
      DXL2 := R1.Left - R2.Left; // Left width of the shadow for child
      DYT2 := R2.Top  - R1.Top;  // Top height of the shadow for child
    end;

    FormJob.Left := Left + Width  - DXR1 - DXL2;
    FormJob.Top := Top - DYT1 + DYT2;

    bCloak := False; // Make form visible
    DwmSetWindowAttribute(FormJob.Handle, DWMWA_CLOAK, @bCloak, SizeOf(bCloak));
  end
  else begin
    FormJob.Left := Left + Width;
    if FormJob.Left + FormJob.Width > Screen.DesktopRect.Right then
      FormJob.Left := Screen.DesktopRect.Right - FormJob.Width;
    FormJob.Top := Top;
    FormJob.Show;
  end;

事实上,这段代码不易读,而且与原始代码相同,但是在将来需要绘制自定义绘图表单时,这会有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接