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

时间:2012-05-03 15:17:14

标签: wpf interop desktop dpi

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

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

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

  1. 我需要引用窗体。
  2. 我需要桌面的DPI设置,并将窗体实际像素转换为WPF虚拟像素。
  3. 我需要一个已经渲染来执行转换的实际Visual实例。
  4. 您是否知道解决上述部分或全部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;
        }
    }
    

    (初始化的)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;
        }
    

    更新

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

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

3 个答案:

答案 0 :(得分: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);

答案 1 :(得分:0)

此代码检查窗口左上角是否在虚拟屏幕框内(包含所有可用屏幕的矩形)。它还负责多监视器设置,其中坐标可以是负数,例如右侧或底部的主监视器。

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;

答案 2 :(得分:0)

我在启动时使用我的WPF项目中剪切的代码;它是用vb.net编写的:

If My.Settings.RememberWindowPositionAndSize Then
    If My.Settings.winMainWinState > 2 Then My.Settings.winMainWinState = WindowState.Normal
    If My.Settings.winMainWinState = WindowState.Minimized Then My.Settings.winMainWinState = WindowState.Normal
    Me.WindowState = My.Settings.winMainWinState
    If My.Settings.winMainWinState = WindowState.Normal Then
        Dim winBounds As New System.Drawing.Rectangle(CInt(My.Settings.winMainPosX), CInt(My.Settings.winMainPosY),
                                                      CInt(My.Settings.winMainSizeB), CInt(My.Settings.winMainSizeH))
        For Each scr As System.Windows.Forms.Screen In System.Windows.Forms.Screen.AllScreens
            If winBounds.IntersectsWith(scr.Bounds) Then
                Me.Width = My.Settings.winMainSizeB
                Me.Height = My.Settings.winMainSizeH
                Me.Top = My.Settings.winMainPosY
                Me.Left = My.Settings.winMainPosX
                Exit For
            End If
        Next
    End If
End If

这里是C#中相同(转换)的代码

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