确定表单是否完全超出屏幕

52

我正在开发一个应用程序,用于记住用户对表单在屏幕上的位置首选项。在某些情况下,用户会将其放在辅助屏幕上,然后稍后启动应用程序而没有第二个屏幕(有时表单会出现在屏幕外)。其他情况下,用户将更改分辨率,从而产生类似的效果。

我希望在Form_Shown事件处理程序中进行此检查。基本上,我想确定表单是否完全超出了屏幕范围,以便重新定位它。

有什么建议吗?


我应该提到,我知道通过获取窗体的屏幕分辨率、大小和位置可以执行一些棘手的逻辑,但我希望有更优雅的解决方案。 - Cody
你真的认为将表单定位、检查是否超出分辨率/大小,然后重新定位更加优雅吗?更优雅的解决方案是,在定位之前先检查是否将其定位到屏幕外。 - Allen Rice
8个回答

71

使用此函数检查表单是否完全显示在屏幕上:

public bool IsOnScreen( Form form )
{
   Screen[] screens = Screen.AllScreens;
   foreach( Screen screen in screens )
   {
      Rectangle formRectangle = new Rectangle( form.Left, form.Top, 
                                               form.Width, form.Height );

      if( screen.WorkingArea.Contains( formRectangle ) )
      {
         return true;
      }
   }

   return false;
}

检查仅顶部左侧点是否在屏幕上:

public bool IsOnScreen( Form form )
{
   Screen[] screens = Screen.AllScreens;
   foreach( Screen screen in screens )
   {
      Point formTopLeft = new Point( form.Left, form.Top );

      if( screen.WorkingArea.Contains( formTopLeft ) )
      {
         return true;
      }
   }

   return false;
}

我忘记如何以其他方式获取表单矩形,但我希望这会有所帮助。 - Andrija
看起来非常整洁,我不知道那个功能存在。不过有一个问题:如果窗口只是部分离开屏幕,那么这仍然会返回false吗?问题要求完全离开屏幕。 - Martin Harris
1
我刚刚试了一下。如果你在if语句上面添加一行screen.WorkingArea.Intersect(formRectangle);,它将确定窗体是否完全离开屏幕。再次感谢Andrija! - Cody
请参见下面Sean的答案,其中包含一些额外的细节,可以使其100%工作。 - Cody
已将所有答案编译成下面的完整答案。 - CrazyTim
显示剩余2条评论

22

结合上述所有解决方案,使用"IntersectsWith"方法和LINQ扩展方法,简而言之:

public bool IsOnScreen(Form form) 
{
   // Create rectangle
   Rectangle formRectangle = new Rectangle(form.Left, form.Top, form.Width, form.Height); 

   // Test
   return Screen.AllScreens.Any(s => s.WorkingArea.IntersectsWith(formRectangle));
}

使用Form.ClientRectangle难道不能更简化吗? - Carsten
1
它适用于大多数情况,但不包括整个表单。MSDN中的Control.ClientRectangle:“控件的客户区域是控件的边界,减去非客户端元素,如滚动条、边框、标题栏和菜单。”(http://msdn.microsoft.com/en-us/library/system.windows.forms.control.clientrectangle.aspx) - Matthias Loerke
3
你可以使用Form.DesktopBounds - Dan Bechard

11

基于所有答案,这里提供完整的解决方案。我添加了一个参数 MinPercentOnScreen,其中至少在所有屏幕/显示器上可见的像素占比为此百分比。因此,如果返回false,您需要将窗口位置移回默认位置。

// Return True if a certain percent of a rectangle is shown across the total screen area of all monitors, otherwise return False.
public bool IsOnScreen(System.Drawing.Point RecLocation, System.Drawing.Size RecSize, double MinPercentOnScreen = 0.2)
{
    double PixelsVisible = 0;
    System.Drawing.Rectangle Rec = new System.Drawing.Rectangle(RecLocation, RecSize);

    foreach (Screen Scrn in Screen.AllScreens)
    {
        System.Drawing.Rectangle r = System.Drawing.Rectangle.Intersect(Rec, Scrn.WorkingArea);
        // intersect rectangle with screen
        if (r.Width != 0 & r.Height != 0)
        {
            PixelsVisible += (r.Width * r.Height);
            // tally visible pixels
        }
    }
    return PixelsVisible >= (Rec.Width * Rec.Height) * MinPercentOnScreen;
}

实现:

return IsOnScreen(this.Location, this.Size);

4

虽然这个帖子有点老,但仍然很有用! Cody和Andrija-感谢您的代码。 我不得不进行一些小的调整: 我使用了formRectangle.Intersect(screen.WorkingArea)而不是screen.WorkingArea.Intersect(formRectangle); 因为Intersect()替换其对象与交集。 如果表单完全脱离屏幕,则交叉口后的formRectangle为(0,0,0,0),并且Contains()返回true。 因此,我还检查formRectangle的Top、Left、Width和Height是否都不为0,然后再返回true。 现在,如果表单的任何部分位于屏幕上,则代码返回true,如果没有部分位于屏幕上,则返回false。


1
谢谢Sean!我们有没有办法将这个答案与Andrija的答案结合起来,形成一个完整的答案? - Cody

1

对于基于Matthias Loerkes答案的WPF

添加对System.Windows.FormsSystem.Drawing的引用。

//using System.Windows.Forms;

public bool IsOnScreen(Window window)
{
   var windowRect = new System.Drawing.Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);
   return Screen.AllScreens.Any(s => s.WorkingArea.IntersectsWith(windowRect));
}

1

如果显示器关闭,这些都不起作用。即使其中一个显示器关闭,Screen.AllScreens函数仍将返回屏幕数量。


至少对于DisplayPort来说,一切都很好。Screen.AllScreens将开始仅返回1个监视器。我的恢复代码运行良好。它将一个窗体窗口移动到主监视器。已测试!但非常感谢这个提示。可能某些视频连接器在这种情况下会出现问题。 - it3xl

0

在定位窗口之前,请检查屏幕分辨率。这将使您能够确定是否将其放置在分辨率范围之外,而不是实际上这样做。


0
使用DesktopBounds的一行解决方案:
Array.Exists(Screen.AllScreens, screen => screen.WorkingArea.Contains(DesktopBounds))

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