How can I display a form on a secondary monitor?
I am writing a screensaver in Delphi. I want is to display a TpresentationFrm on each monitor, fullscreen. To this e开发者_如何学Pythonnd, I have written the following (incomplete) program:
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.
When the ssmDisplay
code is executed, two forms are indeed created (yes, I have exactly two monitors). But they both appear on the first monitor (index 0, but not the primary one).
When stepping through the code, I see that the Screen.Monitors[i].BoundsRect
are correct, but for some reason the forms obtain incorrect bounds:
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))
The first form get the desired position, but the second does not. Instead of going from x=0 to 1920, it occupies x=-1920 to 0, i.e. it appears on the first monitor, above the first form. What is wrong? What is the proper procedure to accomplish what I want?
The form has to be visible in order to set the bounds using BoundRect.
Reverse the lines like this:
presentationForms[i].Visible := true;
presentationForms[i].BoundsRect := Screen.Monitors[i].BoundsRect;
Apparently I try to set the position prematurely.
Replace the for
loop block with
Application.CreateForm(TpresentationFrm, presentationForms[i]);
presentationForms[i].Tag := i;
presentationForms[i].Visible := true;
and then write
procedure TpresentationFrm.FormShow(Sender: TObject);
begin
BoundsRect := Screen.Monitors[Tag].BoundsRect;
end;
Note: You will have problems on high DPI monitors IF your application does not include the highdpi aware flag in its manifest. In this case, Windows will report a wrong (virtualised) bound rectangle.
One solution would be to manually move the form to the screen you, want like this:
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;
This step I succeeded
For example, we want to display the form on the second monitor, its index is 1
program ARPMandiri;
uses
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;
精彩评论