我正在使用WPF应用程序中的代码,该代码需要计算Windows中每个监视器的DPI缩放大小。我能够确定主屏幕的DPI,但由于某些原因,我无法弄清楚如何获得其他显示器的比例尺-其他所有显示器都返回与主显示器相同的DPI。
有一些代码可以做到这一点,所以请多多包涵。第一组代码处理基于HWND的DPI。该代码获取 Activity 的监视器,然后检索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比率才能使Window渲染在正确的位置和大小。
只要DPI相同,这一切都可以在主监视器或多台监视器上正常工作。
问题在于,即使传递给它的
GetDpiForMonitor()
值不同,对HMONITOR
的调用也总是返回主监视器DPI。DPI意识
这是一个WPF应用程序,因此该应用程序支持DPI,但WPF是在System DPI Awareness中运行的,而不是在Per Monitor DPI Aware中运行的。为此,我在启动时挂接了
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,以防有人有兴趣 checkout 此文件。
有什么想法可以可靠地获得系统上所有监视器的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/