我有一个TForm,并将“位置”设置为poMainFormCenter。
当我打开该表格时,它正确显示在主表格的中央。
但是,在多个屏幕(2个监视器)上,当我将应用程序放在辅助监视器中时,该窗体未显示在主窗体的中央。
它仍显示在位于屏幕边缘的主监视器中。
我的应用程序没有任何花哨的地方,我只设置了Position属性。
任何人都知道如何解决此问题吗?
我正在使用Delphi 7和Windows XP SP3。
Jlouro除了注视鼠标外,有正确的主意。 Screen.Monitors []包含每个屏幕上的信息。
我有一个标准程序,该程序会遍历监视器列表,并找出左上角的位置,以确定要戴上的监视器。虽然我的代码没有居中(我只是在确保窗口完全位于它出现的监视器之后),但是想法还是一样的。请注意,您必须考虑以下情况:窗口未显示在ANY监视器上-我将其扔到第一个监视器上来处理。 (当保存的位置位于不再存在的监视器上-移走或在其他计算机上运行时,就会发生这种情况。)
自从我搞砸以来已经很长时间了,它多年来没有给我带来任何麻烦,所以我没有在XP / Delphi 7以后的任何版本上对其进行过测试。
请注意,这仅是确保表单可见并且完全在一个监视器上,没有试图将其居中。
Function PointInBox(x, y, x1, y1, x2, y2 : Integer) : Boolean;
Begin
Result := (X >= X1) And (X <= X2) And (Y >= Y1) And (Y <= Y2);
End;
Function Overlapping(x11, y11, x12, y12, x21, y21, x22, y22 : Integer) : Boolean;
Var
tx1, ty1, tx2, ty2 : Integer;
Begin
Tx1 := Max(x11, x21);
Tx2 := Min(x12, x22);
Ty1 := Max(y11, y21);
Ty2 := Min(y12, y22);
Result := (Tx1 < Tx2) And (Ty1 < Ty2);
End;
Function GetWhere(Form : TForm) : Integer;
Var
Loop : Integer;
Where : Integer;
Begin
Where := -1;
For Loop := 1 to Screen.MonitorCount do
With Screen.Monitors[Loop - 1] do
If PointInBox(Form.Left, Form.Top, Left, Top, Left + Width - 1, Top + Height - 1) then
Where := Loop - 1;
If Where = -1 then // Top left corner is wild, check for anything
For Loop := 1 to Screen.MonitorCount do
With Screen.Monitors[Loop - 1] do
If Overlapping(Form.Left, Form.Top, Form.Left + Form.Width - 1, Form.Top + Form.Height - 1, Left, Top, Left + Width - 1, Top + Height - 1) then
Where := Loop - 1;
Result := Where;
End;
Procedure GetLimits(Where : Integer; var X, Y, WWidth, WHeight : Integer);
Var
R : TRect;
Begin
If Where < 0 then
Begin
SystemParametersInfo(Spi_GetWorkArea, 0, @R, 0);
X := R.Left;
Y := R.Top;
WWidth := R.Right - R.Left + 1;
WHeight := R.Bottom - R.Top + 1;
End
Else With Screen.Monitors[Where] do
Begin
X := Left;
Y := Top;
WWidth := Width;
WHeight := Height;
End;
End;
Procedure EnsureValidDisplay(Form : TForm);
Var
Left : Integer;
Top : Integer;
Width : Integer;
Height : Integer;
Where : WindowPlacement;
Begin
GetLimits(GetWhere(Form), Left, Top, Width, Height);
Where.Length := SizeOf(Where);
Where.Flags := 0;
GetWindowPlacement(Form.Handle, @Where);
If Form.Left < Left then
Where.rcNormalPosition.Left := Left
Else If Form.Left + Form.Width > Left + Width then
Where.rcNormalPosition.Left := Left + Width - Form.Width;
If Form.Top < Top then
Where.rcNormalPosition.Top := Top
Else If Form.Top + Form.Height > Top + Height then
Where.rcNormalPosition.Top := Top + Height - Form.Height;
If Form.Width > Width then
Where.rcNormalPosition.Right := Where.rcNormalPosition.Left + Width
Else
Where.rcNormalPosition.Right := Where.rcNormalPosition.Left + Form.Width;
If Form.Height > Height then
Where.rcNormalPosition.Bottom := Where.rcNormalPosition.Top + Height
Else
Where.rcNormalPosition.Bottom := Where.rcNormalPosition.Top + Form.Height;
SetWindowPlacement(Form.Handle, @Where);
End;
这里没有其他答案首先提到问题的原因,这是VCL中的错误。从我系统上的forms.pas,为了简洁起见,进行了一些剪裁:
procedure TCustomForm.CMShowingChanged(var Message: TMessage);
var
X, Y: Integer;
NewActiveWindow: HWnd;
CenterForm: TCustomForm;
begin
if (FPosition = poScreenCenter) or
((FPosition = poMainFormCenter) and (FormStyle = fsMDIChild)) then
begin
if FormStyle = fsMDIChild then
begin
X := (Application.MainForm.ClientWidth - Width) div 2;
Y := (Application.MainForm.ClientHeight - Height) div 2;
end else
begin
X := (Screen.Width - Width) div 2;
Y := (Screen.Height - Height) div 2;
end;
if X < 0 then X := 0;
if Y < 0 then Y := 0;
SetBounds(X, Y, Width, Height);
if Visible then SetWindowToMonitor;
end
else if FPosition in [poMainFormCenter, poOwnerFormCenter] then
begin
CenterForm := Application.MainForm;
if (FPosition = poOwnerFormCenter) and (Owner is TCustomForm) then
CenterForm := TCustomForm(Owner);
if Assigned(CenterForm) then
begin
X := ((CenterForm.Width - Width) div 2) + CenterForm.Left;
Y := ((CenterForm.Height - Height) div 2) + CenterForm.Top;
end else
begin
X := (Screen.Width - Width) div 2;
Y := (Screen.Height - Height) div 2;
end;
if X < 0 then X := 0;
if Y < 0 then Y := 0;
SetBounds(X, Y, Width, Height);
if Visible then SetWindowToMonitor;
end
else if FPosition = poDesktopCenter then
begin
if FormStyle = fsMDIChild then
begin
X := (Application.MainForm.ClientWidth - Width) div 2;
Y := (Application.MainForm.ClientHeight - Height) div 2;
end else
begin
X := (Screen.DesktopWidth - Width) div 2;
Y := (Screen.DesktopHeight - Height) div 2;
end;
if X < 0 then X := 0;
if Y < 0 then Y := 0;
SetBounds(X, Y, Width, Height);
end;
此错误的关键似乎是以下代码片段,在功能中重复了几次:
if X < 0 then X := 0;
if Y < 0 then Y := 0;
因此,如果您尝试将表单在监视器上居中放置在主监视器的左侧或上方(请记住原点在主监视器的左上角),它将通过此检查被对齐到主监视器。似乎在更新VCL以支持多个监视器时,此代码并未更新。这很有趣,因为后面两行是对SetWindowToMonitor
的调用。
该代码可能是从Windows 95 / Windows NT 4.0中仅支持单个监视器时开始的。在单显示器环境中,负坐标始终在屏幕外,因此捕捉到始终为正的屏幕坐标是有意义的。但是,该代码在存在多个监视器的情况下严重失败,从而导致屏幕坐标为负。
解决该错误的问题留给读者作为练习。有许多可能的解决方案。
我在创建事件上使用它:
C_FollowMouse :BOOLEAN=TRUE; // Global Const - Follow mouse. Opens App in the monitor where the mouse is.
C_Monitor :BYTE=0; // Default Monitor
Procedure TfrmMain.ScreenPOS;
Var pt:tpoint;
_lMonitor :BYTE;
Begin
if NOT Screen.MonitorCount > 1 then Begin
Position := poScreenCenter;
Exit;
End;
_lMonitor := C_Monitor;
if C_FollowMouse then Begin
_lMonitor := 0;
getcursorpos(pt);
if pt.X < 0 then
_lMonitor := 1;
End;
Left:= Screen.Monitors[_lMonitor].Left + Round( (Screen.Monitors[_lMonitor].Width - Width ) / 2);
Top:=Screen.Monitors[_lMonitor].Top + Round( (Screen.Monitors[_lMonitor].Height - Height ) / 2)
End;
只需使用2台显示器进行测试。是我所有的。如果还有更多内容,请发布更改。
我能够通过使用下面的OnActivate表单上的代码来解决此问题:
Self.Left:= MainForm.Left +((MainForm.Width div 2)-(Self.Width div 2));Self.Top:= MainForm.Top +(((MainForm.Height div 2)-(Self.Height div 2));
MainForm是应用程序的“主要”形式。
嘿,大卫,您可以使用poOwnerFormCenter代替poMainFormCenter。它将解决您的问题。阅读此post。
我知道这是一个旧线程,但是我只是想解决有关模式对话框形式的问题,并发现以下内容可以正常工作(在阅读了James Johnson的上述文章之后]
在OnActivate上:
X := (Application.MainForm.ClientWidth - Width) div 2;
Y := (Application.MainForm.ClientHeight - Height) div 2;
self.SetBounds(x,y,self.width,self.height);