Determine if an open WPF window is visible on any monitor

霸气de小男生 提交于 2019-12-12 08:38:58

问题


Is there a way to determine if an open WPF window is currently visible in any of the desktop's connected monitors? By visible I mean that the window's bounds rectangle intersects with the desktop rectangle of any of the monitors.

I need this functionality to determine if a window needs to be repositioned because the monitor configuration (working areas bounds, monitor count) changed between restarts of my application (which saves window positions).

I have come up with the code below and it seems to work, but it has several problems:

  1. I need to reference windows forms.
  2. I need the desktop's DPI settings and transform the windows forms actual pixels to WPF virtual pixels.
  3. I need an acutal Visual instance that already has been rendered to perform the transformation.

Do you know of a solution that gets rid of some or all of the 3 issues above?

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

Usage of the (initialized) Desktop class:

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

Update

Using the virtual screen (SystemParameters.VirtualScreen*) does not work because when using multiple monitors the "desktop" is not a simple rectangle. It might be a polygon. There will be blind spots in the virtual screen because

  1. the connected screens can have different resolutions
  2. you can configure the position of each screen.

回答1:


The code we use to do something similar uses information from SystemParameters, in particular SystemParameter.VirtualScreenLeft, Top, Width and Height.

If we have a saved Location and Size then we determine if the window is out of bounds like this:

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



回答2:


This code checks if a window top left corner is inside the Virtual Screen box (the rectangle that includes all the available screens). It also takes care of multimonitor set ups where coordinates can be negative, for example primary monitor on the right or on the bottom.

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;



回答3:


I use this code snipped in my WPF project on startup; it's written in 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

and here is same (converted) code in 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;
            }
        }
    }
}


来源:https://stackoverflow.com/questions/10434338/determine-if-an-open-wpf-window-is-visible-on-any-monitor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!