如何在WPF中获取当前屏幕的尺寸?

114
我知道可以使用以下方法获取主屏幕的尺寸:
System.Windows.SystemParameters.PrimaryScreenWidth;
System.Windows.SystemParameters.PrimaryScreenHeight;

但是我如何获取当前屏幕的大小?(多屏用户并不总是使用主屏幕,而且并不是所有屏幕都使用相同的分辨率,对吗?)

能够从XAML访问大小会很好,但从代码(C#)中获取也可以。


1
定义“current”。一个窗口可以同时出现在多个屏幕上。 - Jim Balter
16个回答

82

我在 System.Windows.Forms 的屏幕周围创建了一个小包装器,目前一切都正常... 但是我不确定“设备独立像素”怎么处理。

public class WpfScreen
{
    public static IEnumerable<WpfScreen> AllScreens()
    {
        foreach (Screen screen in System.Windows.Forms.Screen.AllScreens)
        {
            yield return new WpfScreen(screen);
        }
    }

    public static WpfScreen GetScreenFrom(Window window)
    {
        WindowInteropHelper windowInteropHelper = new WindowInteropHelper(window);
        Screen screen = System.Windows.Forms.Screen.FromHandle(windowInteropHelper.Handle);
        WpfScreen wpfScreen = new WpfScreen(screen);
        return wpfScreen;
    }

    public static WpfScreen GetScreenFrom(Point point)
    {
        int x = (int) Math.Round(point.X);
        int y = (int) Math.Round(point.Y);

        // are x,y device-independent-pixels ??
        System.Drawing.Point drawingPoint = new System.Drawing.Point(x, y);
        Screen screen = System.Windows.Forms.Screen.FromPoint(drawingPoint);
        WpfScreen wpfScreen = new WpfScreen(screen);

        return wpfScreen;
    }

    public static WpfScreen Primary
    {
        get { return new WpfScreen(System.Windows.Forms.Screen.PrimaryScreen); }
    }

    private readonly Screen screen;

    internal WpfScreen(System.Windows.Forms.Screen screen)
    {
        this.screen = screen;
    }

    public Rect DeviceBounds
    {
        get { return this.GetRect(this.screen.Bounds); }
    }

    public Rect WorkingArea
    {
        get { return this.GetRect(this.screen.WorkingArea); }
    }

    private Rect GetRect(Rectangle value)
    {
        // should x, y, width, height be device-independent-pixels ??
        return new Rect
                   {
                       X = value.X,
                       Y = value.Y,
                       Width = value.Width,
                       Height = value.Height
                   };
    }

    public bool IsPrimary
    {
        get { return this.screen.Primary; }
    }

    public string DeviceName
    {
        get { return this.screen.DeviceName; }
    }
}

SystemParameters.PrimaryScreenHeight 获取的是“设备独立像素”。不幸的是,这并不是获取“设备独立像素”,而是物理像素。唉。 - Mark Lopez
1
我喜欢这个。当然它需要一些工作,但我并不指望能够找到100%的解决方案。 - jeff
4
非常好用。我只是扩展了GetRect方法,并使其返回设备独立像素中的矩形:private Rect GetRect(Rectangle value) { var pixelWidthFactor = SystemParameters.WorkArea.Width / this.screen.WorkingArea.Width; var pixelHeightFactor = SystemParameters.WorkArea.Height / this.screen.WorkingArea.Height; return new Rect { X = value.X * pixelWidthFactor, Y = value.Y * pixelHeightFactor, Width = value.Width * pixelWidthFactor, Height = value.Height * pixelHeightFactor }; } - Jürgen Bayer
1
我相信添加@JürgenBayer的代码将进一步改善您的答案。我曾经遇到过与设备无关像素有关的问题,而来自Jürgen的代码解决了这个问题。谢谢你们两个。 - Bruno V
4
@Jürgen:我认为您的方法只在特定情况下有效。如果“this.screen”的宽高比与主屏幕不同(而您的方法始终使用主屏幕作为参考,而不是当前屏幕),则将错误地获得不同的宽高比缩放因子,导致屏幕尺寸出错。如果当前屏幕的 DPI 设置与主屏幕不同,则其边界将全部错误。在我的系统上,返回的每个 Rect 值都是(极其)不正确的。 - wilford
显示剩余5条评论

51
我还需要当前屏幕尺寸,特别是工作区尺寸,该尺寸不包括任务栏宽度。
我用它来重新定位窗口,使其打开到鼠标位置的右下方。由于窗口相当大,在许多情况下超出了屏幕范围。以下代码基于@e-j的答案:这将给您当前屏幕...。不同之处在于我还展示了我的重新定位算法,我认为这才是关键。
代码如下:
using System.Windows;
using System.Windows.Forms;

namespace MySample
{

    public class WindowPostion
    {
        /// <summary>
        /// This method adjust the window position to avoid from it going 
        /// out of screen bounds.
        /// </summary>
        /// <param name="topLeft">The requiered possition without its offset</param>
        /// <param name="maxSize">The max possible size of the window</param>
        /// <param name="offset">The offset of the topLeft postion</param>
        /// <param name="margin">The margin from the screen</param>
        /// <returns>The adjusted position of the window</returns>
        System.Drawing.Point Adjust(System.Drawing.Point topLeft, System.Drawing.Point maxSize, int offset, int margin)
        {
            Screen currentScreen = Screen.FromPoint(topLeft);
            System.Drawing.Rectangle rect = currentScreen.WorkingArea;

            // Set an offset from mouse position.
            topLeft.Offset(offset, offset);

            // Check if the window needs to go above the task bar, 
            // when the task bar shadows the HUD window.
            int totalHight = topLeft.Y + maxSize.Y + margin;

            if (totalHight > rect.Bottom)
            {
                topLeft.Y -= (totalHight - rect.Bottom);

                // If the screen dimensions exceed the hight of the window
                // set it just bellow the top bound.
                if (topLeft.Y < rect.Top)
                {
                    topLeft.Y = rect.Top + margin;
                }
            }

            int totalWidth = topLeft.X + maxSize.X + margin;
            // Check if the window needs to move to the left of the mouse, 
            // when the HUD exceeds the right window bounds.
            if (totalWidth > rect.Right)
            {
                // Since we already set an offset remove it and add the offset 
                // to the other side of the mouse (2x) in addition include the 
                // margin.
                topLeft.X -= (maxSize.X + (2 * offset + margin));

                // If the screen dimensions exceed the width of the window
                // don't exceed the left bound.
                if (topLeft.X < rect.Left)
                {
                    topLeft.X = rect.Left + margin;
                }
            }

            return topLeft;
        }
    }
}

一些解释:

1) topLeft - position of the top left at the desktop (works                     
   for multi screens - with different aspect ratio).                            
            Screen1              Screen2                                        
        ─  ┌───────────────────┐┌───────────────────┐ Screen3                   
        ▲  │                   ││                   │┌─────────────────┐  ─     
        │  │                   ││                   ││   ▼-            │  ▲     
   1080 │  │                   ││                   ││                 │  │     
        │  │                   ││                   ││                 │  │ 900 
        ▼  │                   ││                   ││                 │  ▼     
        ─  └──────┬─────┬──────┘└──────┬─────┬──────┘└──────┬────┬─────┘  ─     
                 ─┴─────┴─            ─┴─────┴─            ─┴────┴─             
           │◄─────────────────►││◄─────────────────►││◄───────────────►│        
                   1920                 1920                1440                
   If the mouse is in Screen3 a possible value might be:                        
   topLeft.X=4140 topLeft.Y=195                                                 
2) offset - the offset from the top left, one value for both                    
   X and Y directions.                                                          
3) maxSize - the maximal size of the window - including its                     
   size when it is expanded - from the following example                        
   we need maxSize.X = 200, maxSize.Y = 150 - To avoid the expansion            
   being out of bound.                                                          

   Non expanded window:                                                         
   ┌──────────────────────────────┐ ─                                           
   │ Window Name               [X]│ ▲                                           
   ├──────────────────────────────┤ │                                           
   │         ┌─────────────────┐  │ │ 100                                       
   │  Text1: │                 │  │ │                                           
   │         └─────────────────┘  │ │                                           
   │                         [▼]  │ ▼                                           
   └──────────────────────────────┘ ─                                           
   │◄────────────────────────────►│                                             
                 200                                                            

   Expanded window:                                                             
   ┌──────────────────────────────┐ ─                                           
   │ Window Name               [X]│ ▲                                           
   ├──────────────────────────────┤ │                                           
   │         ┌─────────────────┐  │ │                                           
   │  Text1: │                 │  │ │                                           
   │         └─────────────────┘  │ │ 150                                       
   │                         [▲]  │ │                                           
   │         ┌─────────────────┐  │ │                                           
   │  Text2: │                 │  │ │                                           
   │         └─────────────────┘  │ ▼                                           
   └──────────────────────────────┘ ─                                           
   │◄────────────────────────────►│                                             
                 200                                                            
4) margin - The distance the window should be from the screen                   
   work-area - Example:                                                          
   ┌─────────────────────────────────────────────────────────────┐ ─            
   │                                                             │ ↕ Margin     
   │                                                             │ ─            
   │                                                             │              
   │                                                             │              
   │                                                             │              
   │                          ┌──────────────────────────────┐   │              
   │                          │ Window Name               [X]│   │              
   │                          ├──────────────────────────────┤   │              
   │                          │         ┌─────────────────┐  │   │              
   │                          │  Text1: │                 │  │   │              
   │                          │         └─────────────────┘  │   │              
   │                          │                         [▲]  │   │              
   │                          │         ┌─────────────────┐  │   │              
   │                          │  Text2: │                 │  │   │              
   │                          │         └─────────────────┘  │   │              
   │                          └──────────────────────────────┘   │ ─            
   │                                                             │ ↕ Margin     
   ├──────────────────────────────────────────────────┬──────────┤ ─            
   │[start] [♠][♦][♣][♥]                              │en│ 12:00 │              
   └──────────────────────────────────────────────────┴──────────┘              
   │◄─►│                                                     │◄─►│              
    Margin                                                    Margin            

* Note that this simple algorithm will always want to leave the cursor          
  out of the window, therefor the window will jumps to its left:                
  ┌─────────────────────────────────┐        ┌─────────────────────────────────┐
  │                  ▼-┌──────────────┐      │  ┌──────────────┐▼-             │
  │                    │ Window    [X]│      │  │ Window    [X]│               │
  │                    ├──────────────┤      │  ├──────────────┤               │
  │                    │       ┌───┐  │      │  │       ┌───┐  │               │
  │                    │  Val: │   │  │ ->   │  │  Val: │   │  │               │
  │                    │       └───┘  │      │  │       └───┘  │               │
  │                    └──────────────┘      │  └──────────────┘               │
  │                                 │        │                                 │
  ├──────────────────────┬──────────┤        ├──────────────────────┬──────────┤
  │[start] [♠][♦][♣]     │en│ 12:00 │        │[start] [♠][♦][♣]     │en│ 12:00 │
  └──────────────────────┴──────────┘        └──────────────────────┴──────────┘
  If this is not a requirement, you can add a parameter to just use             
  the margin:                                                                   
  ┌─────────────────────────────────┐        ┌─────────────────────────────────┐
  │                  ▼-┌──────────────┐      │                ┌─▼-───────────┐ │
  │                    │ Window    [X]│      │                │ Window    [X]│ │
  │                    ├──────────────┤      │                ├──────────────┤ │
  │                    │       ┌───┐  │      │                │       ┌───┐  │ │
  │                    │  Val: │   │  │ ->   │                │  Val: │   │  │ │
  │                    │       └───┘  │      │                │       └───┘  │ │
  │                    └──────────────┘      │                └──────────────┘ │
  │                                 │        │                                 │
  ├──────────────────────┬──────────┤        ├──────────────────────┬──────────┤
  │[start] [♠][♦][♣]     │en│ 12:00 │        │[start] [♠][♦][♣]     │en│ 12:00 │
  └──────────────────────┴──────────┘        └──────────────────────┴──────────┘
* Supports also the following scenarios:
  1) Screen over screen:
       ┌─────────────────┐  
       │                 │
       │                 │
       │                 │
       │                 │
       └─────────────────┘
     ┌───────────────────┐ 
     │                   │ 
     │  ▼-               │ 
     │                   │ 
     │                   │ 
     │                   │ 
     └──────┬─────┬──────┘ 
           ─┴─────┴─       
  2) Window bigger than screen hight or width
     ┌─────────────────────────────────┐        ┌─────────────────────────────────┐ 
     │                                 │        │ ┌──────────────┐                │
     │                                 │        │ │ Window    [X]│                │
     │                  ▼-┌────────────│─┐      │ ├──────────────┤ ▼-             │
     │                    │ Window    [│]│      │ │       ┌───┐  │                │
     │                    ├────────────│─┤ ->   │ │  Val: │   │  │                │ 
     │                    │       ┌───┐│ │      │ │       └───┘  │                │
     │                    │  Val: │   ││ │      │ │       ┌───┐  │                │
     │                    │       └───┘│ │      │ │  Val: │   │  │                │
     ├──────────────────────┬──────────┤ │      ├──────────────────────┬──────────┤
     │[start] [♠][♦][♣]     │en│ 12:00 │ │      │[start] [♠][♦][♣]     │en│ 12:00 │
     └──────────────────────┴──────────┘ │      └──────────────────────┴──────────┘
                          │       ┌───┐  │        │       └───┘  │
                          │  Val: │   │  │        └──────────────┘
                          │       └───┘  │
                          └──────────────┘


     ┌─────────────────────────────────┐             ┌─────────────────────────────────┐     
     │                                 │             │                                 │ 
     │                                 │             │ ┌───────────────────────────────│───┐
     │    ▼-┌──────────────────────────│────────┐    │ │ W▼-dow                        │[X]│
     │      │ Window                   │     [X]│    │ ├───────────────────────────────│───┤
     │      ├──────────────────────────│────────┤    │ │       ┌───┐      ┌───┐      ┌─┤─┐ │
     │      │       ┌───┐      ┌───┐   │  ┌───┐ │ -> │ │  Val: │   │ Val: │   │ Val: │ │ │ │
     │      │  Val: │   │ Val: │   │ Va│: │   │ │    │ │       └───┘      └───┘      └─┤─┘ │
     │      │       └───┘      └───┘   │  └───┘ │    │ └───────────────────────────────│───┘
     ├──────────────────────┬──────────┤────────┘    ├──────────────────────┬──────────┤
     │[start] [♠][♦][♣]     │en│ 12:00 │             │[start] [♠][♦][♣]     │en│ 12:00 │     
     └──────────────────────┴──────────┘             └──────────────────────┴──────────┘     
  • 我别无选择,只能使用代码格式(否则将会丢失空格)。
  • 最初在上面的代码中此处出现为<remark><code>...</code></remark>

32
我认为你需要一枚金牌来表彰你所做的解释和说明。 - Walter Verhoeven
12
那个插图真的太美了! - Ash

27

嘿,伙计。这将为您提供工作区域的宽度和高度。

System.Windows.SystemParameters.WorkArea.Width
System.Windows.SystemParameters.WorkArea.Height

22
获取主显示器工作区的大小。- 这不是我要找的内容... - Nils

23

调用this.CurrentScreen()可获取基于窗口左上角的当前屏幕信息。

using System.Windows;
using System.Windows.Forms;

namespace Common.Helpers
{
    public static class WindowHelpers
     {
        public static Screen CurrentScreen(this Window window)
         {
             return Screen.FromPoint(new System.Drawing.Point((int)window.Left,(int)window.Top));
         }
     }
}

3
根据您调用辅助函数的窗口左上角位置,此函数返回当前屏幕。但是,根据我的答案得分,我似乎漏掉了一些内容。 - E.J.
@jim-balter 点赞 - 实际上这是最好的答案,我需要屏幕以便获取工作区,并确保我的对话框不超出界限,我将在此处发布我的解决方案。向 E.J. 致敬,因为他的回答快速简洁。 - Juv
^ 奇怪的注释。 - Jim Balter

17
据我所知,WPF没有本地方法来获取给定显示器的尺寸。相反,您可以使用PInvoke本地多显示器函数,将其封装在托管类中(或使用微软官方的CsWin32 Nuget包),并公开您需要从XAML中使用的所有属性。

这正是我担心的——需要 P/Invoke 那些东西或以某种方式访问 System.Windows.Forms.Screen。而且在这样做时,我总是需要计算“设备独立像素”……不过还是谢谢。 - Nils
是的...也许SystemParameters.ConvertPixel()函数会对你有所帮助。它是内部函数,但Reflector并不关心 :)... - Anvaka

12

我知道这是一个老问题,但由于WPF仍然没有提供一个很好的“开箱即用”的方法来实现此功能,而且上面的答案似乎有点过于复杂,希望您会发现下面的解决方案更容易理解一些...

  • 友好的WPF方式,即返回与设备无关的单位(而不是WinForm样式的像素)
  • 支持具有不同DPI的监视器
  • 适应任何大小/位置的任务栏
  • System.Windows.Window的扩展方法。

享受 :)

using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using Point = System.Drawing.Point;

namespace ClrVpin.Shared
{
    public static class WindowExtensions
    {
        public static Rect GetCurrentScreenWorkArea(this Window window)
        {
            var screen = Screen.FromPoint(new Point((int) window.Left, (int) window.Top));
            var dpiScale = VisualTreeHelper.GetDpi(window);

            return new Rect {Width = screen.WorkingArea.Width / dpiScale.DpiScaleX, Height = screen.WorkingArea.Height / dpiScale.DpiScaleY};
        }
    }
}

很棒的解决方案! - MrZweistein

5
为什么不直接使用这个?
var interopHelper = new WindowInteropHelper(System.Windows.Application.Current.MainWindow);
var activeScreen = Screen.FromHandle(interopHelper.Handle);

1
屏幕是Windows.Forms而不是WPF - 但这只是一个起点。如果您看一下我当时使用的解决方案(https://dev59.com/WXI-5IYBdhLWcg3wSGUB#2118993),这正是我所做的 - 但是我包装了System.Windows.Forms.Screen以处理设备无关像素。 - Nils

4
我看到这篇文章,发现没有一个答案完全能够捕捉到我想做的事情。我的笔记本电脑的分辨率是3840x2160,还有两个分辨率为1920x1080的显示器。为了在我的WPF应用程序中获得正确的监视器大小,我必须使应用程序支持DPI(点每英寸)。然后我使用Win32 API获取了监视器的大小。
我首先将窗口移动到我想要获取大小的监视器上。然后通过获取应用程序MainWindow的hwnd(不一定是主窗口,但我的应用程序只有一个窗口)和指向监视器的IntPtr。接着我创建了MONITORINFOEX结构体的新实例,并调用了GetMonitorInfo方法。
MONITORINFOEX结构体包含屏幕的工作区和完整分辨率,因此您可以返回所需的任何一个。这也将允许您省略对System.Windows.Forms的引用(假设您的应用程序不需要它)。我使用了.NET Framework中的参考源代码来查找System.Windows.Forms.Screen的解决方案。
public System.Drawing.Size GetMonitorSize()
{
    var window = System.Windows.Application.Current.MainWindow;
    var hwnd = new WindowInteropHelper(window).EnsureHandle();
    var monitor = NativeMethods.MonitorFromWindow(hwnd, NativeMethods.MONITOR_DEFAULTTONEAREST);
    NativeMethods.MONITORINFO info = new NativeMethods.MONITORINFO();
    NativeMethods.GetMonitorInfo(new HandleRef(null, monitor), info);
    return info.rcMonitor.Size;
}

internal static class NativeMethods
{
    public const Int32 MONITOR_DEFAULTTONEAREST = 0x00000002;

    [DllImport("user32.dll")]
    public static extern IntPtr MonitorFromWindow(IntPtr handle, Int32 flags);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool GetMonitorInfo(HandleRef hmonitor, MONITORINFO info);
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
    public class MONITORINFO
    {
        internal int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
        internal RECT rcMonitor = new RECT();
        internal RECT rcWork = new RECT();
        internal int dwFlags = 0;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;

        public RECT(int left, int top, int right, int bottom)
        {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }

        public RECT(System.Drawing.Rectangle r)
        {
            left = r.Left;
            top = r.Top;
            right = r.Right;
            bottom = r.Bottom;
        }

        public static RECT FromXYWH(int x, int y, int width, int height) => new RECT(x, y, x + width, y + height);

        public System.Drawing.Size Size => new System.Drawing.Size(right - left, bottom - top);
    }
}

在尝试对一个被缩放到除了 100% 以外的尺寸的显示器进行屏幕捕获时,@tstephansen 的解决方案完美地解决了问题。 - mpg

3

花点时间浏览一下SystemParameters成员。

  • VirtualScreenWidth(虚拟屏幕宽度)
  • VirtualScreenHeight(虚拟屏幕高度)

这些甚至考虑到了屏幕的相对位置。

仅在两个监视器上进行过测试。


9
Dana - 我没有测试过,但是 VirtualScreen* 不会返回所有屏幕的完整尺寸吗?- 我特别需要一个屏幕的尺寸(当前窗口所在的屏幕)。 - Nils
1
VirtualScreen似乎是指所有屏幕的大小。 - Thomas
1
其中的一个功能返回了我所有4个屏幕组合后的尺寸大小。 - DJ van Wyk

2
如果您熟悉使用System.Windows.Forms类,那么您可以将System.Windows.Forms类的引用添加到您的项目中:

解决方案资源管理器 -> 引用 -> 添加引用... -> (程序集:框架) -> 向下滚动并检查System.Windows.Forms程序集 -> 确定

现在,您可以添加using System.Windows.Forms;语句,并像以前一样在wpf项目中使用屏幕。

这绝对是最简单的解决方案。我想知道 - 除了添加一个相当大的程序集之外,是否有任何不这样做的好理由? - AeonOfTime

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