现在1920x1080以上分辨率的高分屏电脑渐渐普及了。我们会在Windows的显示设置里看到缩放比例的设置。在Windows桌面客户端的开发中,有时会想要精确计算窗口的面积或位置。然而在默认情况下,无论WinForms的Screen.Bounds.Width属性还是WPF中SystemParameters.PrimaryScreenWidth属性,以下图举例,将会返回除以150%的数值1280。而不是真实的物理分辨率1920。

接下来介绍如何获取Display resolution中显示的实际分辨率。通过如下Win32 API的调用:

        [DllImport("gdi32.dll", EntryPoint = "GetDeviceCaps", SetLastError = true)]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

该方法可以获取设备的硬件信息,可以通过第二个参数nIndex来指定要查询的具体信息。例如我们要用到的以像素为单位的桌面高度DESKTOPVERTRES。

        enum DeviceCap
{
VERTRES = 10,
PHYSICALWIDTH = 110,
SCALINGFACTORX = 114,
DESKTOPVERTRES = 117, // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
}

在获得物理像素高度后,通过计算不难得出屏幕的缩放比列。

        private static double GetScreenScalingFactor()
{
var g = Graphics.FromHwnd(IntPtr.Zero);
IntPtr desktop = g.GetHdc();
var physicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES); var screenScalingFactor =
(double)physicalScreenHeight / Screen.PrimaryScreen.Bounds.Height;
//SystemParameters.PrimaryScreenHeight; return screenScalingFactor;
}

在获取屏幕缩放比例后,诸如通过Graphics类的CopyFromScreen方法来截屏,或者精确控制窗口大小和位置才得以正确实现。
其实在WinForms程序中,我们还有更简单的方式来实现类似效果。即在工程中添加app.manifest文件,将<dipAware>节点的值设为true。这样修改后,Screen.PrimaryScreen.Bounds将获得实际的物理分辨率尺寸,同时你还会发现WinForms程序不糊了。这是因为Windows默认WinForms程序不支持DPI感知,在高分屏下就直接粗暴的把窗体放大。

  <application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>

该设置对WPF无效,WPF默认支持DPI感知功能。而从UWP开始Windows客户端技术全面支持高分屏即高DPI缩放。老旧应用程序不肯升级,以至无法支持高分屏,这锅某软背着挺冤的……
所以同学们,现在开始全面转向WinUI 3吧,这货是这么些年某软兜兜转转,客户端技术集大成者。用Windows APP SDK创建Unpackged App时,恍惚间仿佛回到了在XP上装.NET Runtime的时光。
因为Github访问时常抽风,我将示例代码在Gitee上也同步了一份:

How to get Windows display scale using C#. (github.com)
How to get Windows display scale using C#. (gitee.com)

C#获取Windows10屏幕的缩放比例的更多相关文章

  1. android获取设备屏幕大小的方法

    // 通过WindowManager获取 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay( ...

  2. Android 修改toast的默认位置和获取当前屏幕的高度和宽度

    Toast toast; toast=Toast.makeText(this, "toast", Toast.LENGTH_LONG); toast.setGravity(grav ...

  3. WINFORM中几句程序获取整个屏幕的图片及当前窗口的图片快照

    /// <summary> /// 获取整个屏幕的图片        /// </summary>        /// <returns></returns ...

  4. Android编程心得-在任意类中获取当前屏幕宽高

    进行Android编程时,很多时候都需要获取当前屏幕的宽度与高度,但是当我们需要在别的类中调用屏幕宽高时,直接用原来的方法是不行的,下面我来介绍如何在任意类中调用宽度高度的两种方法. public v ...

  5. ios中获取当前屏幕尺寸的方法

    //获取当前屏幕尺寸 CGRect screenFrame = [UIScreen mainScreen].bounds; int screenWidth = screenFrame.size.wid ...

  6. Python + Appium 获取当前屏幕的截图方法的封装

    使用方法:get_screenshot_as_file(filename),来自于selenium\webdriver\remote\webdiver.py def take_screenShot(s ...

  7. C# 获取当前屏幕的宽高和位置

    上一篇博客<C# 获取当前屏幕DPI>,介绍了如何获取当前屏幕的DPI设置 本章主要介绍如何获取当前窗口所在屏幕的信息 当前屏幕信息 如果当前是单屏幕,可以直接获取主屏幕 var prim ...

  8. 获取系统屏幕尺寸参数的类WxHxD

    获取系统屏幕尺寸参数的类WxHxD 源码: // // WxHxD.h // PM2.5 // // Created by YouXianMing on 14/10/29. // Copyright ...

  9. C#获取显示器屏幕数量 控制winform显示到哪一个屏幕

    获取当前系统连接的屏幕数量: Screen.AllScreens.Count();获取当前屏幕的名称:string CurrentScreenName = Screen.FromControl(thi ...

随机推荐

  1. 【Azure 环境】用 PowerShell 调用 AAD Token, 以及调用Azure REST API(如资源组列表)

    问题描述 PowerShell 脚本调用Azure REST API, 但是所有的API都需要进行权限验证.要在请求的Header部分带上Authorization参数,并用来对List Resour ...

  2. Django笔记&教程 总目录

    本篇博客只有目录,正文内容在目录章节链接的博客里 除目录本身外,没有链接的章节,说明内容还没开始编辑 本项目笔记仍在不断创作中,还有些内容会根据自身所学不断更新完善 本项目主要为markdwon文档, ...

  3. [loj2339]通道

    类似于[loj2553] 对第一棵树边分治,对第二棵树建立虚树,并根据直径合并的性质来处理第三棵树(另外在第三棵树中计算距离需要使用dfs序+ST表做到$o(1)$优化) 总复杂度为$o(n\log^ ...

  4. [luogu4259]寻找车位

    考虑一个分治的做法:按行分治,将所有区间分为两类--经过分割线的.在左/右区间内部,后者显然可以递归下取,考虑前者 先求出出该行上每一列向上和向下的最大长度,记作$up_{i}$和$down_{i}$ ...

  5. 分布式事务(七)之Seata简介

    在前面的文章中,我们介绍了分布式事务的概念以及一些解决方案.fenSeata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata将为用户提供了AT.TCC.SAGA和 ...

  6. myeclipse激活、破解教程

    myeclipse安装注意事项 首先要下载jdk 配置jdk的环境变量 如果1和2 都打不开说明没有下载jdk文件,点击下载就可以了,点击1的时候会出现要求下载的界面,直接下载就可以了...下载完成之 ...

  7. Linux设置默认的声卡

    首先查看自己电脑上的声卡 使用命令行查看 orangepi@orangepi3:~$ ll /proc/asound/ total 0 dr-xr-xr-x 4 root root 0 Dec 23 ...

  8. BehaviorTree.CPP行为树BT的装饰器节点(五)

    Decorators 装饰器是只能有一个子项的节点. 由装饰者来决定是否,何时以及对子节点进行tick. InverterNode tick子节点一次,如果子节点失败则返回SUCCESS,如果孩子成功 ...

  9. Codeforces 1500E - Subset Trick(线段树)

    Codeforces 题目传送门 & 洛谷题目传送门 一道线段树的套路题(似乎 ycx 会做这道题?orzorz!!11) 首先考虑什么样的 \(x\) 是"不合适"的,我 ...

  10. CF1493E Enormous XOR

    题目传送门. 题意简述:给出长度为 \(n\) 的二进制数 \(l,r\),求 \(\max_{l\leq x\leq y\leq r}\oplus_{i=x}^yi\). 非常搞笑的题目,感觉难度远 ...