C# 获取系统DPI缩放比例以及分辨率大小

Posted BoiledYakult

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 获取系统DPI缩放比例以及分辨率大小相关的知识,希望对你有一定的参考价值。

在桌面缩放比例不是100%下的真实桌面分辨率获取方案

一般方法

System.Windows.Forms.Screen

// 获取当前主屏幕分辨率
int screenWidth = Screen.PrimaryScreen.Bounds.Width;
int screenHeight = Screen.PrimaryScreen.Bounds.Height;

// 获取指定屏幕分辨率
Screen secondaryScreen = Screen.AllScreens[1];
int secondaryScreenWidth = secondaryScreen.Bounds.Width;
int secondaryScreenHeight = secondaryScreen.Bounds.Height;

System.Windows.SystemParameters

// 获取当前主屏幕分辨率
double screenWidth = SystemParameters.PrimaryScreenWidth;
double screenHeight = SystemParameters.PrimaryScreenHeight;

// 获取所有屏幕的分辨率
double virtualScreenWidth = SystemParameters.VirtualScreenWidth;
double virtualScreenHeight = SystemParameters.VirtualScreenHeight;

虚拟屏幕是指所有物理屏幕组合成的逻辑屏幕,可以用于跨越多个物理屏幕显示应用程序。

这两个方法都可以在正常情况下获取到屏幕的分辨率 - 当桌面缩放比例不是 100% 的时候获取到的分辨率就是“真实”的分辨率了,而是按缩放比例调整以后屏幕显示的内容的宽度和高度。

Windows API

一开始写了个只获取 DPI 缩放比例的,然后自己手动乘一下,但是调用System.Windows.Interop的时候在同事电脑上找不到这个命名空间,不知道什么原因,后来找到了一篇类似功能的文章,微调了一下:

using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace ScreenDPIHelper

    public class ScreenDPIHelper
    
        #region Win32 API

        [DllImport("user32.dll")]
        static extern IntPtr GetDC(IntPtr ptr);
        [DllImport("gdi32.dll")]
        static extern int GetDeviceCaps(
        IntPtr hdc, // handle to DC
        int nIndex // index of capability
        );
        [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
        static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);

        #endregion

        #region DeviceCaps - 设备属性 常量

        const int HORZRES = 8;
        const int VERTRES = 10;
        const int LOGPIXELSX = 88;
        const int LOGPIXELSY = 90;
        const int DESKTOPVERTRES = 117;
        const int DESKTOPHORZRES = 118;

        #endregion

        #region 属性

        // 获取屏幕分辨率当前物理大小
        public static Size WorkingArea
        
            get
            
                IntPtr hdc = GetDC(IntPtr.Zero);
                Size size = new Size();
                size.Width = GetDeviceCaps(hdc, HORZRES);
                size.Height = GetDeviceCaps(hdc, VERTRES);
                ReleaseDC(IntPtr.Zero, hdc);
                return size;
            
        
        // 当前系统DPI_X 大小 一般为96
        public static int DpiX
        
            get
            
                IntPtr hdc = GetDC(IntPtr.Zero);
                int DpiX = GetDeviceCaps(hdc, LOGPIXELSX);
                ReleaseDC(IntPtr.Zero, hdc);
                return DpiX;
            
        
        // 当前系统DPI_Y 大小 一般为96
        public static int DpiY
        
            get
            
                IntPtr hdc = GetDC(IntPtr.Zero);
                int DpiX = GetDeviceCaps(hdc, LOGPIXELSY);
                ReleaseDC(IntPtr.Zero, hdc);
                return DpiX;
            
        
        // 获取真实设置的桌面分辨率大小
        public static Size DesktopResolution
        
            get
            
                IntPtr hdc = GetDC(IntPtr.Zero);
                Size size = new Size();
                size.Width = GetDeviceCaps(hdc, DESKTOPHORZRES);
                size.Height = GetDeviceCaps(hdc, DESKTOPVERTRES);
                ReleaseDC(IntPtr.Zero, hdc);
                return size;
            
        
        // 获取宽度缩放百分比
        public static float ScaleX
        
            get
            
                IntPtr hdc = GetDC(IntPtr.Zero);
                float ScaleX = (float)GetDeviceCaps(hdc, DESKTOPHORZRES) / (float)GetDeviceCaps(hdc, HORZRES);
                ReleaseDC(IntPtr.Zero, hdc);
                return ScaleX;
            
        
        // 获取高度缩放百分比
        public static float ScaleY
        
            get
            
                IntPtr hdc = GetDC(IntPtr.Zero);
                float ScaleY = (float)(float)GetDeviceCaps(hdc, DESKTOPVERTRES) / (float)GetDeviceCaps(hdc, VERTRES);
                ReleaseDC(IntPtr.Zero, hdc);
                return ScaleY;
            
        

        #endregion
    

这个类用到了user32.dllgdi32.dll这两个Win32动态链接库,并调用了其中的函数。如:

  • GetDC: 该函数返回指定窗口客户区域或屏幕的设备上下文(DC)。
  • ReleaseDC: 该函数释放由GetDC函数获得的指定设备上下文(DC)。
  • GetDeviceCaps: 该函数检索指定设备的某些功能,如分辨率,颜色深度,打印机输出分辨率等。

定义的常量参数分别为:

  • HORZRES:水平方向分辨率。
  • VERTRES:垂直方向分辨率。
  • LOGPIXELSX:水平方向 DPI。
  • LOGPIXELSY:垂直方向 DPI。
  • DESKTOPVERTRES:真实的桌面分辨率的垂直大小。
  • DESKTOPHORZRES:真实的桌面分辨率的水平大小。

参数的值是对应参数在 Win32 API 中的索引。

可获取的参数分别是:

  • WorkingArea:获取屏幕分辨率的物理大小,也就是去掉任务栏等占据屏幕空间后的大小。
  • DpiX:获取当前系统水平方向的 DPI ,DPI 是一个表示每英寸点数的度量单位,通常为 96。
  • DpiY:获取当前系统垂直方向的 DPI 。
  • DESKTOP:获取真实的桌面分辨率大小,包括任务栏等占据空间的部分。
  • ScaleX:获取宽度的缩放比例,即当前屏幕的实际宽度与标准宽度(DESKTOPHORZRES)的比值。
  • ScaleY:获取高度的缩放比例,即当前屏幕的实际高度与标准高度(DESKTOPVERTRES)的比值。

参考文档:

平台调用示例

用平台调用封送数据

标识 DLL 中的函数

C# API 获取系统DPI缩放倍数跟分辨率大小

WPF 不同DPI下,窗口大小的处理

在设置桌面不同分辨率以及较大DPI下,窗口如何显示的问题。(此例中仅设置高度)

前端:

  1. 设置窗口内容自适应SizeToContent="WidthAndHeight"
  2. 添加ViewBox -- 设置默认不拉伸Stretch="None",当DPI超大时如超过1920*1080p的175%(即win10默认不支持的比例显示),开启ViewBox缩放
  3. 顶层布局容器RootGrid添加高宽最大值和最小值。
 1 <Window x:Class="WindowHeightChangedForDpi.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:WindowHeightChangedForDpi"
 7         mc:Ignorable="d"
 8         Title="MainWindow" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen">
 9     <Viewbox x:Name="RootViewbox" Stretch="None">
10         <Grid x:Name="RootGrid" Width="1000" MaxHeight="680" MinHeight="520" ClipToBounds="True">
11 
12         </Grid>
13     </Viewbox>
14 </Window>

后台:

  1. 添加对Loaded事件的监听,并在之后注销。窗口只需要首次初始其高度即可。
  2. 获取屏幕的高度和任务栏的高度 -- 具体可以参考C# 获取当前屏幕的宽高和位置
  3. 比较当前可显示高度(屏幕高度-任务栏高度)与窗口的最大/最小高度,然后设置当前窗口的实际高度。
  4. 如果可显示高度比最小值还小,则开启ViewBox内容缩放。ViewBox的高度为当前可显示高度。
  5. 如果当前窗口有阴影,可设置阴影高度大小。保证窗口在可显示区域内正常显示。
 1     public partial class MainWindow : Window
 2     {
 3         public MainWindow()
 4         {
 5             InitializeComponent();
 6             Loaded += InitWindowActualHeight_OnLoaded;
 7         }
 8 
 9         #region 设置窗口对屏幕高度的自适应
10 
11         private void InitWindowActualHeight_OnLoaded(object sender, RoutedEventArgs e)
12         {
13             Loaded -= InitWindowActualHeight_OnLoaded;
14             InitWindowActualHeight();
15         }
16 
17         private const double WindowShadowHeight = 0;
18 
19         private void InitWindowActualHeight()
20         {
21             //获取任务栏高度
22             var taskbarHeight = SystemParameters.PrimaryScreenHeight - SystemParameters.WorkArea.Height;
23             //获取窗体所在屏幕的高度
24             var height = GetScreenHeight();
25             var visibleAreaHeight = height - taskbarHeight;
26 
27             //可显示高度 > 窗口最大高度
28             if (visibleAreaHeight > RootGrid.MaxHeight + WindowShadowHeight)
29             {
30                 //设置高度等于最大高度
31                 RootGrid.Height = RootGrid.MaxHeight;
32             }
33             //可显示高度 < 窗口最小高度
34             else if (visibleAreaHeight < RootGrid.MinHeight + WindowShadowHeight)
35             {
36                 //设置Viewbox高度=可视高度-阴影高度(此处通过绽放显示窗口,所以不能通过设置窗口或者设置内容的高度来实现)
37                 RootViewbox.Height = visibleAreaHeight - WindowShadowHeight;
38                 //等比例缩小
39                 RootViewbox.Stretch = Stretch.Uniform;
40             }
41             else
42             {
43                 //设置高度等于最小高度
44                 RootGrid.Height = RootGrid.MinHeight;
45             }
46         }
47         const double DpiPercent = 96;
48         private double GetScreenHeight()
49         {
50             var intPtr = new WindowInteropHelper(this).Handle;//获取当前窗口的句柄
51             var screen = Screen.FromHandle(intPtr);//获取当前屏幕
52 
53             double height = 0;
54             using (Graphics currentGraphics = Graphics.FromHwnd(intPtr))
55             {
56                 double dpiXRatio = currentGraphics.DpiX / DpiPercent;
57                 double dpiYRatio = currentGraphics.DpiY / DpiPercent;
58                 height = screen.Bounds.Height / dpiYRatio;
59                 //var width = screen.Bounds.Width / dpiXRatio;
60                 //var left = screen.Bounds.Left / dpiXRatio;
61                 //var top = screen.Bounds.Top / dpiYRatio;
62             }
63             return height;
64         }
65         #endregion

 注:获取的屏幕高度为屏幕像素,需要转换为WPF单位。

以上是关于C# 获取系统DPI缩放比例以及分辨率大小的主要内容,如果未能解决你的问题,请参考以下文章

windows DPi缩放

Windows:缩放设置及DPI缩放详解

Windows:缩放设置及DPI缩放详解

系统缩放比例多少合适

CefSharp应用——High DPI问题

win10 dpi设置是啥意思