wpf C# 为 Windows 中的每个监视器获取 DPI 缩放
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/41531438/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# Getting DPI Scaling for each Monitor in Windows
提问by Rick Strahl
I'm working with code in a WPF application that needs to figure out the DPI scaling size for each monitor in Windows. I'm able to figure out the DPI of the primary screen but for some reason I cannot figure out how to get the scale for other monitors - the others all return the same DPI as the main monitor.
我正在处理 WPF 应用程序中的代码,该应用程序需要确定 Windows 中每个显示器的 DPI 缩放大小。我能够弄清楚主屏幕的 DPI,但由于某种原因,我无法弄清楚如何获得其他显示器的比例 - 其他显示器都返回与主显示器相同的 DPI。
There's a bit of code to do this so bear with me. The first set of code deals with getting the DPI based on an HWND. The code gets the active monitor and then retrieves the DPI settings and compares figures out a ratio to the 96 DPI (typically 100%).
有一些代码可以做到这一点,所以请耐心等待。第一组代码处理基于 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,
}
This code is used as part of a screen capture solution where there's supposed to be an overlay over the window the user's mouse is over. I capture the mouse position and based on that I get a pixel location and I then create the WPF window there. Here I have to apply the DPI ratio in order to get the Window to render in the right place and size.
此代码用作屏幕捕获解决方案的一部分,其中应该在用户鼠标所在的窗口上覆盖。我捕获鼠标位置并基于此获取像素位置,然后在那里创建 WPF 窗口。在这里,我必须应用 DPI 比率才能使窗口在正确的位置和大小上呈现。
This all works fine on the primary monitor or on multiple monitors as long as the DPI is the same.
只要 DPI 相同,这一切都可以在主显示器或多台显示器上正常工作。
The problem is that the call to GetDpiForMonitor()always returns the primary monitor DPI even though the HMONITORvalue passed to it is different.
问题在于,GetDpiForMonitor()即使HMONITOR传递给它的值不同,调用也始终返回主监视器 DPI 。
DPI Awareness
新闻部意识
This is a WPF application so the app is DPI aware, but WPF runs in System DPI Awareness, rather than Per Monitor DPI Aware. To that effect I hooked up static App()code on startup to explicitly set to per monitor 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]
I see that the DPI setting changes to Process_Per_Monitor_DPI_Awarebut that also seems to have no effect on the behavior. I still see the DPI results returned as the same as the main monitor.
我看到 DPI 设置更改为,Process_Per_Monitor_DPI_Aware但这似乎也对行为没有影响。我仍然看到返回的 DPI 结果与主监视器相同。
There's a test in a largish solution that allows playing with this here: https://github.com/RickStrahl/MarkdownMonster/blob/master/Tests/ScreenCaptureAddin.Test/DpiDetectionTests.csin case anyone is interested in checking this out.
在一个更大的解决方案中有一个测试,允许在这里玩这个:https: //github.com/RickStrahl/MarkdownMonster/blob/master/Tests/ScreenCaptureAddin.Test/DpiDetectionTests.cs以防有人有兴趣检查这个。
Any ideas how I can reliably get the DPI Scaling level for all monitors on the system (and why the heck is there no system API or even a WMI setting for this)?
我有什么想法可以可靠地获得系统上所有监视器的 DPI 缩放级别(以及为什么没有系统 API 甚至 WMI 设置)?
采纳答案by mm8
WPF has per-monitor DPI support since .NET Framework 4.6.2. There is more information and an example available at GitHub: http://github.com/Microsoft/WPF-Samples/tree/master/PerMonitorDPI.
自 .NET Framework 4.6.2 起,WPF 就具有每个显示器的 DPI 支持。GitHub 上提供了更多信息和示例:http: //github.com/Microsoft/WPF-Samples/tree/master/PerMonitorDPI。
You may also want to check out the VisualTreeHelper.GetDpimethod.
您可能还想查看VisualTreeHelper.GetDpi方法。
回答by PhysicalEd
I have been wrestling with similar problems (secondary monitor Screen bounds seemed to be scaled by the same scale factor as set on the primary display), and I found some documentation that seems to at least explain that this is expected behavior:
我一直在努力解决类似的问题(辅助监视器屏幕边界似乎按与主显示器上设置的相同比例因子进行缩放),我发现一些文档似乎至少解释了这是预期行为:
DPI Awareness Mode - System
Windows Version Introduced - Vista
Application's view of DPI - All displays have the same DPI (the DPI of the primary display at the time the Windows session was started)
Behavior on DPI change - Bitmap-stretching (blurry)
DPI 感知模式 -
引入了系统Windows 版本 - Vista
应用程序的 DPI 视图 - 所有显示器都具有相同的 DPI(Windows 会话启动时主显示器的 DPI)
DPI 更改行为 - 位图拉伸(模糊)
This is extracted from the first table in High DPI desktop application development on Windows
这是从Windows 上的高 DPI 桌面应用程序开发中的第一个表中提取的
That's the first documentation I found that at least explicitly spells out that the code will report that all windows share the same scaling when the application is under System DPI Awareness.
这是我发现的第一个文档,至少明确指出,当应用程序处于 System DPI Awareness 下时,代码将报告所有窗口共享相同的缩放比例。

