Search code examples
delphimultiple-monitorsparallelsdelphi-10.4-sydneytform

Screen becomes black when repositioning Form to second monitor using Parallels VM


I am working with Delphi 10.4.2 in Windows 10 (virtualized in Parallels) on a dual monitor system. To recreate the problem on a multi-monitor system, create a new Windows VCL Application and place two buttons on the form: btnPrimaryMonitor and btnSecondaryMonitor. Then insert this code by creating click handlers for the two buttons:

procedure TForm1.btnPrimaryMonitorClick(Sender: TObject);
begin
  RepositionFormToMonitor(0);
  EnableDisableButtons;
end;

procedure TForm1.RepositionFormToMonitor(const aMonitor: Integer);
const
  offset = 2;
begin
  Self.Width := Screen.Monitors[aMonitor].Width - offset;
  Self.Height := Screen.Monitors[aMonitor].Height - offset;

  Self.Top := Screen.Monitors[aMonitor].Top;
  Self.Left := Screen.Monitors[aMonitor].Left;
end;

procedure TForm1.btnSecondaryMonitorClick(Sender: TObject);
begin
  RepositionFormToMonitor(1);
  EnableDisableButtons;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  EnableDisableButtons;
  Self.BorderStyle := bsNone;
  Self.FormStyle := fsStayOnTop;
  RepositionFormToMonitor(0);
end;

procedure TForm1.EnableDisableButtons;
begin
  btnPrimaryMonitor.Enabled := (Self.Monitor.MonitorNum = 1);
  btnSecondMonitor.Enabled := (Self.Monitor.MonitorNum = 0);
end;

This works perfectly, but as soon as I set offset = 1 or offset = 0 the screen becomes black!

The purpose of the code is to reposition the maximized stay-on-top Form from the primary monitor to the secondary monitor by clicking on the btnSecondMonitor button and then back to the primary monitor by clicking on the btnPrimaryMonitor button.

How can this problem be avoided?


Solution

  • A few issues:

    1. You should not set WindowState to wsMaximized. In fact, you shouldn't touch this property at all.

    2. Setting BoundsRect will set Left, Top, Width, and Height, so there is no need to set Left and Top separately.

    3. To go back to the primary monitor, just set the form's BoundsRect.

    Here's an example:

    Create a new VCL project. Set the main form's BorderStyle to bsNone.

    Then add the following code:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      for var i := 0 to Screen.MonitorCount - 1 do
      begin
        var btn := TButton.Create(Self);
        btn.Parent := Self;
        btn.Caption := i.ToString;
        btn.Tag := i;
        btn.OnClick := MonitorButtonClick;
        btn.Top := 8;
        btn.Left := 8 + (btn.Width + 8) * i;
      end;
    end;
    
    procedure TForm1.MonitorButtonClick(Sender: TObject);
    begin
      BoundsRect := Screen.Monitors[(Sender as TButton).Tag].BoundsRect;
    end;
    

    If this code doesn't work properly on your system, you probably have some problem with that Windows system. This should work flawlessly.