2

我有一个 TForm,我将“位置”设置为 poMainFormCenter。

当我打开该表单时,它会正确显示在主表单的中心。

但是,在多个屏幕(2 个监视器)上,当我将应用程序放在辅助监视器中时,该窗体不会显示在主窗体的中心。

它仍显示在主监视器中,位于屏幕边缘。

我的应用程序没有什么花哨的,我只设置了 Position 属性。

有人知道怎么修这个东西吗?

我正在使用 Delphi 7 和 Windows XP SP3。

4

6 回答 6

6

除了看鼠标之外,Jlouro 的想法是正确的。Screen.Monitors[] 包含每个屏幕的信息。

我有一个标准程序,它通过监视器列表并找出左上角的位置来决定将它放在哪个监视器上。虽然我的代码没有居中(我只是在确保窗口完全位于它出现的任何监视器之后),但想法保持不变。请注意,您必须考虑窗口不显示在任何监视器上的情况——我通过将其扔到第一个监视器来处理这种情况。(当保存的位置在一个不再存在的监视器上时,就会发生这种情况——要么被删除,要么在另一台机器上运行。)

自从我搞砸这个已经很长时间了,它并没有给我带来任何麻烦,所以我没有在比 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;
于 2012-04-10T17:31:19.457 回答
3

这里的其他答案都没有首先提到问题的原因,这是 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;

So, if you try to center the form on a monitor to the left or above the primary monitor (remember that origin is at upper-left corner of primary monitor), it will get snapped to the primary monitor from this check. It seems that this code wasn't updated when VCL was updated to support multiple monitors. Which is amusing, since two lines later are calls to SetWindowToMonitor.

The code was probably there from when only single monitors were supported in Windows 95 / Windows NT 4.0. In a single-monitor environment, negative coordinates are always off-screen, and it makes sense to snap to onscreen coordinates, which are always positive. However, the code fails miserably in the presence of multiple monitors, which allows for negative onscreen coordinates.

Working around this bug is left as an exercise to the reader. There are a number of possible solutions.

于 2013-05-01T20:12:43.393 回答
2

我在创建事件中使用它:

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台显示器测试过。是我的全部。如果您有更多,请发回更改。

于 2012-04-10T15:18:31.683 回答
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 是应用程序的“主要”形式。

于 2012-04-19T13:56:35.043 回答
0

Hey David you can use poOwnerFormCenter Instead of poMainFormCenter. It will Solve your problem. Read this post.

于 2015-06-04T09:58:59.737 回答
0

I know this is an old thread but I was just trying to fix this problem with regards to modal dialog forms and found the following to work (after reading the above post by James Johnson)

On OnActivate:

X := (Application.MainForm.ClientWidth - Width) div 2;
Y := (Application.MainForm.ClientHeight - Height) div 2;
self.SetBounds(x,y,self.width,self.height);
于 2020-02-27T10:49:49.987 回答