草庐IT

wpf - C# 在 Windows 中为每个监视器获取 DPI 缩放

coder 2024-06-08 原文

我正在使用 WPF 应用程序中的代码,该应用程序需要计算出 Windows 中每个显示器的 DPI 缩放大小。我能够计算出主屏幕的 DPI,但出于某种原因我不知道如何获得其他显示器的比例 - 其他显示器都返回与主显示器相同的 DPI。

有一些代码可以执行此操作,请耐心等待。第一组代码处理基于 HWND 获取 DPI。该代码获取事件监视器,然后检索 DPI 设置并将数字与 96 DPI(通常为 100%)的比率进行比较。

public static decimal GetDpiRatio(Window window)
{
    var dpi = WindowUtilities.GetDpi(window, DpiType.Effective);
    decimal ratio = 1;
    if (dpi > 96)
        ratio = (decimal)dpi / 96M;

    return ratio;
}
public static decimal GetDpiRatio(IntPtr hwnd)
{            
    var dpi = GetDpi(hwnd, DpiType.Effective);            
    decimal ratio = 1;
    if (dpi > 96)
        ratio = (decimal)dpi / 96M;

    //Debug.WriteLine($"Scale: {factor} {ratio}");
    return ratio;
}

public static uint GetDpi(IntPtr hwnd, DpiType dpiType)
{            
    var screen = Screen.FromHandle(hwnd);            
    var pnt = new Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
    var mon = MonitorFromPoint(pnt, 2 /*MONITOR_DEFAULTTONEAREST*/);

    Debug.WriteLine("monitor handle: " + mon);
    try
    {
        uint dpiX, dpiY;
        GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
        return dpiX;
    }
    catch
    {
        // fallback for Windows 7 and older - not 100% reliable
        Graphics graphics = Graphics.FromHwnd(hwnd);
        float dpiXX = graphics.DpiX;                
        return Convert.ToUInt32(dpiXX);
    }
}


public static uint GetDpi(Window window, DpiType dpiType)
{
    var hwnd = new WindowInteropHelper(window).Handle;
    return GetDpi(hwnd, dpiType);
}     

[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);

[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);        


public enum DpiType
{
    Effective = 0,
    Angular = 1,
    Raw = 2,
}

此代码用作屏幕捕获解决方案的一部分,其中应该在用户鼠标悬停的窗口上覆盖。我捕获鼠标位置并基于该位置获得像素位置,然后在那里创建 WPF 窗口。在这里,我必须应用 DPI 比率以使窗口在正确的位置和大小呈现。

只要 DPI 相同,这在主显示器或多显示器上都可以正常工作。

问题是对 GetDpiForMonitor() 的调用总是返回主监视器 DPI,即使传递给它的 HMONITOR 值不同。

DPI 意识

这是一个 WPF 应用程序,因此该应用程序是 DPI 感知的,但 WPF 在系统 DPI 感知中运行,而不是在每个监视器 DPI 感知中运行。为此,我在启动时连接了 static App() 代码以明确设置为每个监视器 DPI:

    try
    {
        // for this to work make sure [assembly:dpiawareness
        PROCESS_DPI_AWARENESS awareness;
        GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
        var result = SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware);
        GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
}

[DllImport("SHCore.dll", SetLastError = true)]
public static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);

[DllImport("SHCore.dll", SetLastError = true)]
public static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness);

public enum PROCESS_DPI_AWARENESS
{
    Process_DPI_Unaware = 0,
    Process_System_DPI_Aware = 1,
    Process_Per_Monitor_DPI_Aware = 2
}

// and in assemblyinfo
[assembly: DisableDpiAwareness]

我看到 DPI 设置更改为 Process_Per_Monitor_DPI_Aware 但这似乎对行为也没有影响。我仍然看到返回的 DPI 结果与主显示器相同。

在一个较大的解决方案中有一个测试,允许在这里玩这个: https://github.com/RickStrahl/MarkdownMonster/blob/master/Tests/ScreenCaptureAddin.Test/DpiDetectionTests.cs如果有人有兴趣检查一下。

有什么想法可以可靠地获得系统上所有显示器的 DPI 缩放级别(以及为什么没有系统 API 甚至没有 WMI 设置)?

最佳答案

自 .NET Framework 4.6.2 以来,WPF 具有针对每个监视器的 DPI 支持。 GitHub 上提供了更多信息和示例:http://github.com/Microsoft/WPF-Samples/tree/master/PerMonitorDPI .

您可能还想查看 VisualTreeHelper.GetDpi方法。

关于wpf - C# 在 Windows 中为每个监视器获取 DPI 缩放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41531438/

有关wpf - C# 在 Windows 中为每个监视器获取 DPI 缩放的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  3. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  4. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  5. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  6. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  7. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  8. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  9. ruby - 将n维数组的每个元素乘以Ruby中的数字 - 2

    在Ruby中,是否有一种简单的方法可以将n维数组中的每个元素乘以一个数字?这样:[1,2,3,4,5].multiplied_by2==[2,4,6,8,10]和[[1,2,3],[1,2,3]].multiplied_by2==[[2,4,6],[2,4,6]]?(很明显,我编写了multiplied_by函数以区别于*,它似乎连接了数组的多个副本,不幸的是这不是我需要的)。谢谢! 最佳答案 它的长格式等价物是:[1,2,3,4,5].collect{|n|n*2}其实并没有那么复杂。你总是可以使你的multiply_by方法:c

  10. ruby - 如何在 Ruby 中执行 Windows CLI 命令? - 2

    我在目录“C:\DocumentsandSettings\test.exe”中有一个文件,但是当我用单引号编写命令时`C:\DocumentsandSettings\test.exe(我无法在此框中显示),用于在Ruby中执行命令,我无法这样做,我收到的错误是找不到文件或目录。我尝试用“//”和“\”替换“\”,但似乎没有任何效果。我也使用过系统、IO.popen和exec命令,但所有的努力都是徒劳的。exec命令还使程序退出,这是我不想发生的。提前致谢。 最佳答案 反引号环境就像双引号,所以反斜杠用于转义。此外,Ruby会将空格解

随机推荐