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

确定打开的WPF窗口是否在任何监视器上可见,wpf,interop,desktop,dpi,Wpf,Interop,Desktop,Dpi,有没有办法确定当前是否在任何桌面连接的监视器中可以看到打开的WPF窗口?“可见”是指窗口的边界矩形与任何监视器的桌面矩形相交 我需要此功能来确定是否需要重新定位窗口,因为在重新启动应用程序(保存窗口位置)之间监视器配置(工作区域边界、监视器计数)发生了更改 我已经想出了下面的代码,它似乎可以工作,但它有几个问题: 我需要参考windows窗体 我需要桌面的DPI设置,并将windows窗体的实际像素转换为WPF虚拟像素 我需要一个已经渲染的acutal Visual实例来执行转换 你知道解决上述

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

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

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

  • 我需要参考windows窗体
  • 我需要桌面的DPI设置,并将windows窗体的实际像素转换为WPF虚拟像素
  • 我需要一个已经渲染的acutal Visual实例来执行转换
  • 你知道解决上述三个问题的方法吗

    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;
        }
    }
    
    更新

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

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

  • 我们用于执行类似操作的代码使用来自的信息,特别是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);
    
    bool出边界=
    
    (location.X此代码检查窗口左上角是否位于虚拟屏幕框(包含所有可用屏幕的矩形)内。它还负责坐标为负数的多监视器设置,例如右侧或底部的主监视器

    bool valid_position =
    SystemParameters.VirtualScreenLeft <= saved_location.X &&
    (SystemParameters.VirtualScreenLeft + SystemParameters.VirtualScreenWidth) >= saved_location.X &&
    SystemParameters.VirtualScreenTop <= saved_location.Y &&
    (SystemParameters.VirtualScreenTop + SystemParameters.VirtualScreenHeight) >= saved_location.Y;
    
    bool有效位置=
    SystemParameters.VirtualScreenLeft=保存的位置.X&&
    SystemParameters.VirtualScreenTop=保存的位置.Y;
    
    我在启动时使用WPF项目中截取的代码;它是用vb.net编写的:

    如果My.Settings.memberwindowpositionandsize
    如果My.Settings.winMainWinState>2,则My.Settings.winMainWinState=WindowsState.Normal
    如果My.Settings.winMainWinState=WindowState.Minimized,则My.Settings.winMainWinState=WindowState.Normal
    Me.WindowState=My.Settings.winMainWinState
    如果My.Settings.winMainWinState=WindowState.Normal,则
    将winBounds设置为新的System.Drawing.Rectangle(CInt(My.Settings.winMainPosX)、CInt(My.Settings.winMainPosY),
    CInt(My.Settings.winMainSizeB),CInt(My.Settings.winMainSizeH))
    对于每个scr,在System.Windows.Forms.Screen.AllScreens中显示为System.Windows.Forms.Screen
    如果winBounds.IntersectsWith(scr.Bounds),则
    Me.Width=My.Settings.winMainSizeB
    Me.Height=My.Settings.winMainSizeH
    Me.Top=My.Settings.winMainPosY
    Me.Left=My.Settings.winMainPosX
    退出
    如果结束
    下一个
    如果结束
    如果结束
    
    这是C语言中相同的(转换的)代码#


    尽管该链接中的问题非常相似,但它并没有回答我的问题。我已经提出了一个解决方案,我想找到一种方法来明确地解决我提到的三个问题。然后WPF和标准C#库无法帮助你。你可以尝试PInvoke,但这可能更脏。有关t那:引用System.Windows.Forms有什么问题?这不像是在向bin中添加另一个程序集。我个人的偏好是创建一个使用PInvoke的帮助器类,并保持代码干净可读。使用虚拟屏幕不起作用,因为使用多个监视器时“桌面”不是简单的矩形。虚拟屏幕中会有盲点,因为a)连接的屏幕可以有不同的分辨率,b)可以配置每个屏幕的位置。使用虚拟屏幕不起作用,因为使用多个监视器时,“桌面”不是简单的矩形。虚拟屏幕中会有盲点,因为a)连接的屏幕可以有不同的分辨率,b)您可以配置每个屏幕的位置。现在(Win8+)每个屏幕都可以有自己的DPI设置。WPF使用逻辑96 DPI像素,因此对于任何不使用小字体=96 DPI的屏幕,您的代码都将失败。
    bool valid_position =
    SystemParameters.VirtualScreenLeft <= saved_location.X &&
    (SystemParameters.VirtualScreenLeft + SystemParameters.VirtualScreenWidth) >= saved_location.X &&
    SystemParameters.VirtualScreenTop <= saved_location.Y &&
    (SystemParameters.VirtualScreenTop + SystemParameters.VirtualScreenHeight) >= saved_location.Y;
    
    if (My.Settings.RememberWindowPositionAndSize) {
        if (My.Settings.winMainWinState > 2)
            My.Settings.winMainWinState = WindowState.Normal;
        if (My.Settings.winMainWinState == WindowState.Minimized)
            My.Settings.winMainWinState = WindowState.Normal;
        this.WindowState = My.Settings.winMainWinState;
    
        if (My.Settings.winMainWinState == WindowState.Normal) {
            System.Drawing.Rectangle winBounds = new System.Drawing.Rectangle(Convert.ToInt32(My.Settings.winMainPosX), Convert.ToInt32(My.Settings.winMainPosY), Convert.ToInt32(My.Settings.winMainSizeB), Convert.ToInt32(My.Settings.winMainSizeH));
    
            foreach (System.Windows.Forms.Screen scr in System.Windows.Forms.Screen.AllScreens) {
                if (winBounds.IntersectsWith(scr.Bounds)) {
                    this.Width = My.Settings.winMainSizeB;
                    this.Height = My.Settings.winMainSizeH;
                    this.Top = My.Settings.winMainPosY;
                    this.Left = My.Settings.winMainPosX;
                    break;
                }
            }
        }
    }