确定在任何监视器上是否可以看到打开的WPF窗口

bit*_*onk 7 wpf desktop interop dpi

有没有办法确定在任何桌面连接的监视器中当前是否可以看到打开的WPF窗口?通过可见我的意思是窗口的边界矩形与任何监视器的桌面矩形相交.

我需要此功能来确定是否需要重新定位窗口,因为监视器配置(工作区域边界,监视器计数)在应用程序重新启动(保存窗口位置)之间发生了变化.

我已经提出了下面的代码,它似乎工作,但它有几个问题:

  1. 我需要引用windows窗体.
  2. 我需要桌面的DPI设置,并将窗体实际像素转换为WPF虚拟像素.
  3. 我需要一个已经渲染来执行转换的实际Visual实例.

您是否知道解决上述部分或全部3个问题的解决方案?

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;

internal static class Desktop
{
    private static Size dpiFactor = new Size(1.0, 1.0);
    private static bool isInitialized;

    public static IEnumerable<Rect> WorkingAreas
    {
        get
        {
            return
                Screen.AllScreens.Select(
                    screen =>
                    new Rect(
                        screen.WorkingArea.Left * dpiFactor.Width,
                        screen.WorkingArea.Top * dpiFactor.Height,
                        screen.WorkingArea.Width * dpiFactor.Width,
                        screen.WorkingArea.Height * dpiFactor.Height));
        }
    }

    public static void TryInitialize(Visual visual)
    {
        if (isInitialized)
        {
            return;
        }

        var ps = PresentationSource.FromVisual(visual);
        if (ps == null)
        {
            return;
        }

        var ct = ps.CompositionTarget;
        if (ct == null)
        {
            return;
        }

        var m = ct.TransformToDevice;
        dpiFactor = new Size(m.M11, m.M22);
        isInitialized = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

(初始化)Desktop类的用法:

    private bool IsLocationValid(Rect windowRectangle)
    {
        foreach (var workingArea in Desktop.WorkingAreas)
        {
            var intersection = Rect.Intersect(windowRectangle, workingArea);
            var minVisible = new Size(10.0, 10.0);
            if (intersection.Width >= minVisible.Width && 
                intersection.Height >= minVisible.Height)
            {
                return true;
            }
        }

        return false;
    }
Run Code Online (Sandbox Code Playgroud)

更新

使用虚拟屏幕(SystemParameters.VirtualScreen*)不起作用,因为使用多个监视器时,"桌面"不是简单的矩形.它可能是一个多边形.虚拟屏幕中会出现盲点,因为

  1. 连接的屏幕可以有不同的分辨率
  2. 您可以配置每个屏幕的位置.

Phi*_*hil 7

我们用来做类似事情的代码使用来自SystemParameters的信息,特别是SystemParameter.VirtualScreenLeft,Top,Width和Height.

如果我们有一个保存的位置和大小,那么我们确定窗口是否超出界限:

bool outOfBounds =
    (location.X <= SystemParameters.VirtualScreenLeft - size.Width) ||
    (location.Y <= SystemParameters.VirtualScreenTop - size.Height) ||
    (SystemParameters.VirtualScreenLeft + 
        SystemParameters.VirtualScreenWidth <= location.X) ||
    (SystemParameters.VirtualScreenTop + 
        SystemParameters.VirtualScreenHeight <= location.Y);
Run Code Online (Sandbox Code Playgroud)

  • 使用虚拟屏幕不起作用,因为使用多个显示器时,"桌面"不是简单的矩形.虚拟屏幕中会出现盲点,因为a)连接的屏幕可能具有不同的分辨率,b)您可以配置每个屏幕的位置. (2认同)