C#: 如何获取完整的桌面尺寸?

81

如何查找整个桌面的大小?不是“工作区”和也不是“屏幕分辨率”,这两个属性只涉及一个屏幕。我想找出虚拟桌面的总宽度和高度,其中每个显示器仅显示一部分。


2
也许“完整的桌面尺寸”将取决于屏幕的位置。不确定如何计算,但您仍然可以使用System.Windows.Forms.Screen.AllScreens捕获每个屏幕的数量和分辨率。 - Havenard
11个回答

137
你有两个选项:
  1. PresentationFramework.dll

SystemParameters.VirtualScreenWidth   
SystemParameters.VirtualScreenHeight
  • System.Windows.Forms.dll

    SystemInformation.VirtualScreen.Width   
    SystemInformation.VirtualScreen.Height
    
  • 如果你正在开发 WPF 应用程序,可以使用第一种选项。


    1
    更多像这样的答案!简短、正确且美观大方! - Bitterblue
    我发现在启用缩放时,这些值只会在一个屏幕上注册。可能与硬件有关,尚不确定。 - Kelly Elton
    那很可能是因为在2011年,多桌面缩放还不是一件事情。如果你找到了解决方法,请更新答案,@KellyElton。 - Dennis
    1
    我无法找到解决方案,我认为这是一个错误。我正在使用Blackweb BWA17HO002 USB3至HDMI适配器,配合英特尔HD 520(笔记本电脑)。当笔记本电脑的显示缩放> 150%时,这些值仅返回笔记本电脑屏幕尺寸。一旦我取消缩放,它就会注册第二个监视器。 - Kelly Elton

    35

    我认为是时候用一些LINQ来更新这个答案了,它可以轻松地通过单个表达式获取整个桌面大小。

    Console.WriteLine(
        Screen.AllScreens.Select(screen=>screen.Bounds)
        .Aggregate(Rectangle.Union)
        .Size
    );
    

    我想你想要的是这样的:

    int minx, miny, maxx, maxy;
    minx = miny = int.MaxValue;
    maxx = maxy = int.MinValue;
    
    foreach(Screen screen in Screen.AllScreens){
        var bounds = screen.Bounds;
        minx = Math.Min(minx, bounds.X);
        miny = Math.Min(miny, bounds.Y);
        maxx = Math.Max(maxx, bounds.Right);
        maxy = Math.Max(maxy, bounds.Bottom);
    }
    
    Console.WriteLine("(width, height) = ({0}, {1})", maxx - minx, maxy - miny);
    

    请记住,这并不是全部情况。多个显示器可能会错开或以非矩形的形式排列。因此,并非(minx,miny)和(maxx,maxy)之间的所有空间都可见。

    编辑:

    我刚意识到可以使用Rectangle.Union使代码更简洁:

    Rectangle rect = new Rectangle(int.MaxValue, int.MaxValue, int.MinValue, int.MinValue);
    
    foreach(Screen screen in Screen.AllScreens)
        rect = Rectangle.Union(rect, screen.Bounds);
    
    Console.WriteLine("(width, height) = ({0}, {1})", rect.Width, rect.Height);
    

    3
    我是一名乌克兰程序员。我正在验证这段代码是否能够直接使用,只需要复制粘贴即可。 - Vova Popov

    16

    检查:

    SystemInformation.VirtualScreen.Width
    SystemInformation.VirtualScreen.Height
    

    8
    要获取监视器的物理像素大小,您可以使用以下方法。
    static class DisplayTools
    {
        [DllImport("gdi32.dll")]
        static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
    
        private enum DeviceCap
        {
            Desktopvertres = 117,
            Desktophorzres = 118
        }
    
        public static Size GetPhysicalDisplaySize()
        {
            Graphics g = Graphics.FromHwnd(IntPtr.Zero);
            IntPtr desktop = g.GetHdc();
    
            int physicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.Desktopvertres);
            int physicalScreenWidth = GetDeviceCaps(desktop, (int)DeviceCap.Desktophorzres);
    
            return new Size(physicalScreenWidth, physicalScreenHeight);
        }
    
    
    }
    

    这是第一个报告正确尺寸的程序,但仅适用于单个显示器。 - BrainSlugs83

    7

    这并没有回答问题,只是在窗口点(位置)在所有屏幕中的额外见解上添加了一些内容。

    使用以下代码查找一个点(例如窗口的最后已知位置)是否在整个桌面的范围内。如果不在,则将窗口的位置重置为默认值pBaseLoc

    代码不考虑任务栏或其他工具栏,你需要自己处理。

    示例用法:从站点A保存窗口位置到数据库。用户登录具有2个监视器的站点B并将窗口移动到第二个监视器,然后退出保存新位置。回到站点A,如果不使用上述代码,则不会显示窗口。

    我的进一步解决方案是实现将userID和站点IP(&winLoc)保存到给定应用程序的数据库或本地用户首选项文件中,然后加载该站点和应用程序的用户首选项。

    Point pBaseLoc = new Point(40, 40)
    int x = -500, y = 140;
    Point pLoc = new Point(x, y);
    bool bIsInsideBounds = false;
    
    foreach (Screen s in Screen.AllScreens)
    {
        bIsInsideBounds = s.Bounds.Contains(pLoc);
        if (bIsInsideBounds) { break; }
    }//foreach (Screen s in Screen.AllScreens)
    
    if (!bIsInsideBounds) { pLoc = pBaseLoc;  }
    
    this.Location = pLoc;
    

    6
    如果你知道这并没有回答问题,为什么要把它发布为问题的答案?第一句话不应该引起警觉吗? - Timwi
    2
    另一方面,这正是我在搜索这个主题时寻找的东西。 - Simon Gillbee

    3
    我认为获取“真实”的屏幕尺寸的最佳方式是直接从视频控制器获取值。
        using System;
        using System.Management;
        using System.Windows.Forms;
    
        namespace MOS
        {
            public class ClassMOS
            {
                public static void Main()
                {
                    try
                    {
                        ManagementObjectSearcher searcher = 
                            new ManagementObjectSearcher("root\\CIMV2", 
                            "SELECT * FROM Win32_VideoController"); 
    
                        foreach (ManagementObject queryObj in searcher.Get())
                        {
                            Console.WriteLine("CurrentHorizontalResolution: {0}", queryObj["CurrentHorizontalResolution"]);
                            Console.WriteLine("-----------------------------------");
                            Console.WriteLine("CurrentVerticalResolution: {0}", queryObj["CurrentVerticalResolution"]);
                        }
                    }
                    catch (ManagementException e)
                    {
                        MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
                    }
                }
            }
    }
    

    这应该能胜任工作 ;) 问候...

    1

    该方法通过使用Left和Top的最低值以及Right和Bottom的最高值,返回包含所有屏幕边界的矩形...

    static Rectangle GetDesktopBounds() {
       var l = int.MaxValue;
       var t = int.MaxValue;
       var r = int.MinValue;
       var b = int.MinValue;
       foreach(var screen in Screen.AllScreens) {
          if(screen.Bounds.Left   < l) l = screen.Bounds.Left  ;
          if(screen.Bounds.Top    < t) t = screen.Bounds.Top   ;
          if(screen.Bounds.Right  > r) r = screen.Bounds.Right ;
          if(screen.Bounds.Bottom > b) b = screen.Bounds.Bottom;
       }
       return Rectangle.FromLTRB(l, t, r, b);
    }
    

    请简单解释一下。 - Syeda Zunaira
    它循环遍历所有屏幕。如果左边缘小于变量'l',则将'l'设置为此新值;如果底部边缘大于'b',则设置该值。最终结果是l、t、r、b被设置为整个可见桌面的左、上、右和下边缘。 - dynamichael

    1
    你可以使用System.Drawing中的Bounds。
    你可以创建一个类似这样的函数。
    public System.Windows.Form.Screen[] GetScreens(){
        Screen[] screens = Screen.AllScreens;
        return screens;
    }
    

    然后你可以像这样将屏幕一、二等存储在变量中:

    System.Windows.Form.Screen[] screens = func.GetScreens();
    System.Windows.Form.Screen screen1 = screens[0];
    

    然后你可以获取屏幕的边界:
    System.Drawing.Rectangle screen1Bounds = screen1.Bounds;
    

    使用此代码,您将获得所有属性,例如WidthHeight等。

    1
    获取虚拟显示器的大小,无需任何依赖项。
    public enum SystemMetric
    {
        VirtualScreenWidth = 78, // CXVIRTUALSCREEN 0x0000004E 
        VirtualScreenHeight = 79, // CYVIRTUALSCREEN 0x0000004F 
    }
    
    [DllImport("user32.dll")]
    public static extern int GetSystemMetrics(SystemMetric metric);
    
    public static Size GetVirtualDisplaySize()
    {
        var width = GetSystemMetrics(SystemMetric.VirtualScreenWidth);
        var height = GetSystemMetrics(SystemMetric.VirtualScreenHeight);
    
        return new Size(width, height);
    }
    

    在我看来,最佳答案是没有显式依赖。 - Zintom

    0

    你会发现许多窗口操作尚未可用,或者可能永远不会被提供。我使用PInvoke.User32 nuget包取得了最大的成功。

    var screenX = PInvoke.User32.GetSystemMetrics(PInvoke.User32.SystemMetric.SM_CXSCREEN);
    var screenY = PInvoke.User32.GetSystemMetrics(PInvoke.User32.SystemMetric.SM_CYSCREEN);
    

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