Alex Egorov
Alex Egorov

Reputation: 937

Get size and borders of the Form

I'm trying to align my child form after the main form "side by side", but have some difficulties

To reproduce the problem create new VCL application and add one button to the form:

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;

And result:

Windows 7: enter image description here

Windows 10: enter image description here

After using 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;

And now this is completely aligned on both Windows 7 and Windows 10

Windows 7: enter image description here Windows 10: enter image description here

But to do that I need to show the child form first. If I call DwmGetWindowAttribute for child (and invisible) form before showing I get the same values as for GetWindowRect.It is impossible to get this before showing?

Upvotes: 6

Views: 1863

Answers (1)

Alex Egorov
Alex Egorov

Reputation: 937

Thanks to comment of Jonathan Potter now I have such code and it works:

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;

Actually this code less readable and contain the same as original code, but this can help in the future when it is will be necessary to draw custom drawing form.

Upvotes: 9

Related Questions