Delphi 7:多个监视器上的中心表格位置

时间:2012-04-10 13:16:51

标签: delphi delphi-7

我有一个TForm,我设置了#34;位置"到poMainFormCenter。

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

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

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

我的应用程序没有什么特别之处,我只设置了Position属性。

任何人都知道如何解决这个问题?

我使用的是Delphi 7和Windows XP SP3。

6 个答案:

答案 0 :(得分:5)

除了看鼠标外,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;

答案 1 :(得分:2)

这里没有其他答案首先提到问题的原因,这是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中仅支持单个监视器。在单监视器环境中,负坐标总是在屏幕外,并且捕捉屏幕坐标是有意义的,这些坐标始终是正的。但是,在存在多个监视器的情况下,代码会失败,这允许屏幕上的负坐标。

解决这个错误是留给读者的练习。有许多可能的解决方案。

答案 2 :(得分:1)

我在创建事件上使用它:

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台显示器测试过它。我只有。 如果您有更多,请回复更改。

答案 3 :(得分:1)

我可以通过使用Form 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是应用程序的“主要”形式。

答案 4 :(得分:0)

嗨大卫你可以使用 poOwnerFormCenter 而不是 poMainFormCenter 。它会解决你的问题。阅读此post

答案 5 :(得分:0)

我知道这是一个旧线程,但是我只是想解决有关模式对话框形式的问题,并发现以下内容可以正常工作(在阅读了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);