如何在第二个显示器上显示一个表单?

8

我正在使用Delphi编写屏幕保护程序。我的目标是在每个显示器上全屏显示一个TpresentationFrm窗体。为此,我编写了以下(不完整)程序:

program ScrTemplate;

uses
  ...

{$R *.res}

type
  TScreenSaverMode = (ssmConfig, ssmDisplay, ssmPreview, ssmPassword);

function GetScreenSaverMode: TScreenSaverMode;
begin
  // Some non-interesting code
end;

var
  i: integer;
  presentationForms: array of TpresentationFrm;

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;

  case GetScreenSaverMode of
    ssmConfig:
      Application.CreateForm(TconfigFrm, configFrm);
    ssmDisplay:
      begin
        SetLength(presentationForms, Screen.MonitorCount);
        for i := 0 to high(presentationForms) do
        begin
          Application.CreateForm(TpresentationFrm, presentationForms[i]);
          presentationForms[i].BoundsRect := Screen.Monitors[i].BoundsRect;
          presentationForms[i].Visible := true;
        end;
      end
  else
    ShowMessage(GetEnumName(TypeInfo(TScreenSaverMode), integer(GetScreenSaverMode)));
  end;

  Application.Run;
end.

当执行ssmDisplay代码时,确实创建了两个表单(是的,我有两个显示器)。但它们都出现在第一个显示器上(索引为0,但不是主显示器)。
当逐步执行代码时,我发现Screen.Monitors[i].BoundsRect是正确的,但由于某种原因,表单获取了不正确的边界。
Watch Name                          Value (TRect: Left, Top, Right, Bottom, ...)
Screen.Monitors[0].BoundsRect   (-1680, 0, 0, 1050, (-1680, 0), (0, 1050))
Screen.Monitors[1].BoundsRect   (0, 0, 1920, 1080, (0, 0), (1920, 1080))

presentationForms[0].BoundsRect (-1680, 0, 0, 1050, (-1680, 0), (0, 1050))
presentationForms[1].BoundsRect (-1920, -30, 0, 1050, (-1920, -30), (0, 1050))

第一个表单得到了所需的位置,但第二个表单没有。它不是从x = 0到1920,而是占据了x = -1920到0,即出现在第一个监视器上方的第一个表单上方。错在哪里?完成我想要的正确程序是什么?

如果你的应用程序在其清单中不包含高 DPI 感知标志,那么在高 DPI 显示器上会出现问题。在这种情况下,Windows 将报告一个错误的(虚拟化的)边界矩形。 - Gabriel
4个回答

9
表单必须可见才能使用BoundRect设置边界。
将行反转,如下所示:
presentationForms[i].Visible := true;
presentationForms[i].BoundsRect := Screen.Monitors[i].BoundsRect;

是的,我只需要在“for”循环中交换这两行:先设置可见性,然后再更改边界! - Andreas Rejbrand

2

我似乎试图过早设置位置。

请将 for 循环块替换为:

Application.CreateForm(TpresentationFrm, presentationForms[i]);
presentationForms[i].Tag := i;
presentationForms[i].Visible := true;

然后编写

procedure TpresentationFrm.FormShow(Sender: TObject);
begin
  BoundsRect := Screen.Monitors[Tag].BoundsRect;
end;

1
注意:如果您的应用程序在其清单中不包括高 DPI 感知标志,那么您将在高 DPI 显示器上遇到问题。在这种情况下,Windows 将报告一个错误的(虚拟化的)边界矩形。
一种解决方案是手动将窗体移动到所需的屏幕,如下所示:
procedure MoveFormToScreen(Form: TForm; ScreenNo: Integer);
begin
 Assert(Form.Position= poDesigned);
 Assert(Form.Visible= TRUE);

 Form.WindowState:= wsNormal;
 Form.Top := Screen.Monitors[ScreenNo].Top;
 Form.Left:= Screen.Monitors[ScreenNo].Left;
 Form.WindowState:= wsMaximized;
end;

0

这一步我成功了
例如,我们想要在第二个显示器上显示表单,它的索引是1

 

program ARPMandiri;

使用
Vcl.Forms,
SysUtils,
UMain in 'UMain.pas' {frmMain},
........
..............................;

{$R *.res}

begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
.............
Application.CreateForm(TfrmMain, frmMain);
frmMain.Visible := true;
frmMain.BoundsRect := Screen.Monitors[1].BoundsRect;
ApplyThemes(frmMain);
Application.Run;
end.


procedure TfrmMain.FormCreate(Sender: TObject);
Var
iTm: Integer;
begin
Self.Left:= Screen.Monitors[1].Left;
Self.Top:= Screen.Monitors[1].Top;
Self.Width:= Screen.Monitors[1].Width;
Self.Height:= Screen.Monitors[1].Height;
...............

end;


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