Search code examples
delphiwinapiwindow

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?


Solution

  • 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.