如何在 WPF 中获取 DPI?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1918877/
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
How can I get the DPI in WPF?
提问by tom greene
How can I get the DPI in WPF?
如何在 WPF 中获取 DPI?
回答by Ana Betts
https://docs.microsoft.com/en-us/archive/blogs/jaimer/getting-system-dpi-in-wpf-appseems to work
https://docs.microsoft.com/en-us/archive/blogs/jaimer/getting-system-dpi-in-wpf-app似乎工作
PresentationSource source = PresentationSource.FromVisual(this);
double dpiX, dpiY;
if (source != null) {
dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}
回答by user1681882
var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
var dpiX = (int)dpiXProperty.GetValue(null, null);
var dpiY = (int)dpiYProperty.GetValue(null, null);
回答by rohit21agrawal
With .NET 4.6.2 Preview and higher, you can call VisualTreeHelper.GetDpi(Visual visual)
. It returns a DpiScale
structure, which tells you the DPI at which the given Visual
will be or has been rendered.
使用 .NET 4.6.2 Preview 及更高版本,您可以调用VisualTreeHelper.GetDpi(Visual visual)
. 它返回一个DpiScale
结构,它告诉您Visual
将要或已经呈现给定的 DPI 。
回答by Andreas
The only way I found to get the "real" monitor dpi is the following. All other mentioned techniques just say 96 which is not correct for the most monitors.
我发现获得“真实”显示器 dpi 的唯一方法如下。所有其他提到的技术只是说 96,这对于大多数显示器来说是不正确的。
public class ScreenInformations
{
public static uint RawDpi { get; private set; }
static ScreenInformations()
{
uint dpiX;
uint dpiY;
GetDpi(DpiType.RAW, out dpiX, out dpiY);
RawDpi = dpiX;
}
/// <summary>
/// Returns the scaling of the given screen.
/// </summary>
/// <param name="dpiType">The type of dpi that should be given back..</param>
/// <param name="dpiX">Gives the horizontal scaling back (in dpi).</param>
/// <param name="dpiY">Gives the vertical scaling back (in dpi).</param>
private static void GetDpi(DpiType dpiType, out uint dpiX, out uint dpiY)
{
var point = new System.Drawing.Point(1, 1);
var hmonitor = MonitorFromPoint(point, _MONITOR_DEFAULTTONEAREST);
switch (GetDpiForMonitor(hmonitor, dpiType, out dpiX, out dpiY).ToInt32())
{
case _S_OK: return;
case _E_INVALIDARG:
throw new ArgumentException("Unknown error. See https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx for more information.");
default:
throw new COMException("Unknown error. See https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx for more information.");
}
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062.aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
const int _S_OK = 0;
const int _MONITOR_DEFAULTTONEAREST = 2;
const int _E_INVALIDARG = -2147024809;
}
/// <summary>
/// Represents the different types of scaling.
/// </summary>
/// <seealso cref="https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511.aspx"/>
public enum DpiType
{
EFFECTIVE = 0,
ANGULAR = 1,
RAW = 2,
}
回答by Simon Mourier
I have updated my answer from 2015. Here is some utility code that uses the latest DPI functions from Windows 10 (specifically GetDpiForWindow functionwhich is the only method that supports the DPI_AWARENESS of the window/application/process, etc.) but falls back to older ones (dpi per monitor, and desktop dpi) so it should still work with Windows 7.
我已经从 2015 年更新了我的答案。这里有一些实用程序代码,它使用了 Windows 10 中最新的 DPI 函数(特别是GetDpiForWindow 函数,它是唯一支持窗口/应用程序/进程等的 DPI_AWARENESS 的方法)但回退到较旧的(每台显示器的 dpi 和桌面 dpi),因此它仍然适用于 Windows 7。
It has not dependency on WPF nor Winforms, only on Windows itself.
它不依赖于 WPF 或 Winforms,只依赖于 Windows 本身。
// note this class considers dpix = dpiy
public static class DpiUtilities
{
// you should always use this one and it will fallback if necessary
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow
public static int GetDpiForWindow(IntPtr hwnd)
{
var h = LoadLibrary("user32.dll");
var ptr = GetProcAddress(h, "GetDpiForWindow"); // Windows 10 1607
if (ptr == IntPtr.Zero)
return GetDpiForNearestMonitor(hwnd);
return Marshal.GetDelegateForFunctionPointer<GetDpiForWindowFn>(ptr)(hwnd);
}
public static int GetDpiForNearestMonitor(IntPtr hwnd) => GetDpiForMonitor(GetNearestMonitorFromWindow(hwnd));
public static int GetDpiForNearestMonitor(int x, int y) => GetDpiForMonitor(GetNearestMonitorFromPoint(x, y));
public static int GetDpiForMonitor(IntPtr monitor, MonitorDpiType type = MonitorDpiType.Effective)
{
var h = LoadLibrary("shcore.dll");
var ptr = GetProcAddress(h, "GetDpiForMonitor"); // Windows 8.1
if (ptr == IntPtr.Zero)
return GetDpiForDesktop();
int hr = Marshal.GetDelegateForFunctionPointer<GetDpiForMonitorFn>(ptr)(monitor, type, out int x, out int y);
if (hr < 0)
return GetDpiForDesktop();
return x;
}
public static int GetDpiForDesktop()
{
int hr = D2D1CreateFactory(D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED, typeof(ID2D1Factory).GUID, IntPtr.Zero, out ID2D1Factory factory);
if (hr < 0)
return 96; // we really hit the ground, don't know what to do next!
factory.GetDesktopDpi(out float x, out float y); // Windows 7
Marshal.ReleaseComObject(factory);
return (int)x;
}
public static IntPtr GetDesktopMonitor() => GetNearestMonitorFromWindow(GetDesktopWindow());
public static IntPtr GetShellMonitor() => GetNearestMonitorFromWindow(GetShellWindow());
public static IntPtr GetNearestMonitorFromWindow(IntPtr hwnd) => MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
public static IntPtr GetNearestMonitorFromPoint(int x, int y) => MonitorFromPoint(new POINT { x = x, y = y }, MONITOR_DEFAULTTONEAREST);
private delegate int GetDpiForWindowFn(IntPtr hwnd);
private delegate int GetDpiForMonitorFn(IntPtr hmonitor, MonitorDpiType dpiType, out int dpiX, out int dpiY);
private const int MONITOR_DEFAULTTONEAREST = 2;
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("user32")]
private static extern IntPtr MonitorFromPoint(POINT pt, int flags);
[DllImport("user32")]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, int flags);
[DllImport("user32")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32")]
private static extern IntPtr GetShellWindow();
[StructLayout(LayoutKind.Sequential)]
private partial struct POINT
{
public int x;
public int y;
}
[DllImport("d2d1")]
private static extern int D2D1CreateFactory(D2D1_FACTORY_TYPE factoryType, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr pFactoryOptions, out ID2D1Factory ppIFactory);
private enum D2D1_FACTORY_TYPE
{
D2D1_FACTORY_TYPE_SINGLE_THREADED = 0,
D2D1_FACTORY_TYPE_MULTI_THREADED = 1,
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("06152247-6f50-465a-9245-118bfd3b6007")]
private interface ID2D1Factory
{
int ReloadSystemMetrics();
[PreserveSig]
void GetDesktopDpi(out float dpiX, out float dpiY);
// the rest is not implemented as we don't need it
}
}
public enum MonitorDpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
回答by google dev
Use GetDeviceCaps
function:
使用GetDeviceCaps
功能:
static void Main(string[] args)
{
// 1.25 = 125%
var dpi = GetDpi();
}
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
private static float GetDpi()
{
IntPtr desktopWnd = IntPtr.Zero;
IntPtr dc = GetDC(desktopWnd);
var dpi = 100f;
const int LOGPIXELSX = 88;
try
{
dpi = GetDeviceCaps(dc, LOGPIXELSX);
}
finally
{
ReleaseDC(desktopWnd, dc);
}
return dpi / 96f;
}
回答by Eli
This is how I managed to get a "scale factor" in WPF. My laptop's resolution is 1920x1440.
这就是我设法在 WPF 中获得“比例因子”的方式。我的笔记本电脑的分辨率是 1920x1440。
int resHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; // 1440
int actualHeight = SystemParameters.PrimaryScreenHeight; // 960
double ratio = actualHeight / resHeight;
double dpi = resHeigh / actualHeight; // 1.5 which is true because my settings says my scale is 150%
回答by Alexander Alexander
January 25, 2017 3:54 pm
2017 年 1 月 25 日下午 3:54
"Calling Windows 10 APIs From a Desktop Application" and
“从桌面应用程序调用 Windows 10 API”和
https://docs.microsoft.com/en-us/uwp/api/windows.devices.display.displaymonitor
https://docs.microsoft.com/en-us/uwp/api/windows.devices.display.displaymonitor
"Display?Monitor Class"
“显示器?显示器类”
Namespace: Windows.Devices.Display Assemblies:Windows.Devices.Display.dll, Windows.dll
命名空间:Windows.Devices.Display 程序集:Windows.Devices.Display.dll、Windows.dll
Provides information about a display monitor device connected to the system.
提供有关连接到系统的显示监视器设备的信息。
These data include commonly used information from the monitor's Extended Display Identification Data (EDID, which is an industry-standard display descriptor block that nearly all monitors use to provide descriptions of supported modes and general device information) and DisplayID (which is a newer industry standard that provides a superset of EDID).
这些数据包括来自显示器的扩展显示标识数据(EDID,这是一个行业标准的显示描述符块,几乎所有显示器都用于提供支持模式和一般设备信息的描述)和 DisplayID(这是一个较新的行业标准)中的常用信息它提供了 EDID 的超集)。
Raw?DpiX
Gets the physical horizontal DPI of the monitor (based on the monitor's native resolution and physical size).
Raw?DpiX
获取显示器的物理水平 DPI(基于显示器的原始分辨率和物理尺寸)。
Raw?DpiY
Gets the physical vertical DPI of the monitor (based on the monitor's native resolution and physical size).
Raw?DpiY
获取显示器的物理垂直 DPI(基于显示器的原始分辨率和物理尺寸)。
回答by Alexander Alexander
Basic monitor info in Windows from 2006
2006 年以来 Windows 中的基本监视器信息
https://docs.microsoft.com/en-us/windows/desktop/wmicoreprov/msmonitorclass
https://docs.microsoft.com/en-us/windows/desktop/wmicoreprov/msmonitorclass
MSMonitorClass class
MSMonitorClass 类
WmiMonitorRawEEdidV1Block class
WmiMonitorRawEEdidV1Block 类
WmiMonitorBasicDisplayParams class
WmiMonitorBasicDisplayParams 类
MaxHorizontalImageSize ( EDID byte 21 )
MaxVerticalImageSize ( EDID byte 22 )
( Sizes in EDID are in centimeters above and in millimeters in EDID Detailed Timing Descriptor
( EDID 中的大小以厘米为单位,EDID 详细时序描述符中的以毫米为单位
12 Horizontal image size, mm, 8 lsbits (0–4095 mm, 161 in)
13 Vertical image size, mm, 8 lsbits (0–4095 mm, 161 in)
14 Bits 7–4 Horizontal image size, mm, 4 msbits
Bits 3–0 Vertical image size, mm, 4 msbits
12 水平图像尺寸,mm,8 lsbits(0–4095 mm,161 in)
13 垂直图像尺寸,mm,8 lsbits(0–4095 mm,161 in)
14 位 7–4 水平图像尺寸,mm,4 msbits
位3–0 垂直图像尺寸,mm,4 msbits
)
)
and
和