How can I get DPI in WPF?

How can I get DPI in WPF?

+58
dpi wpf
Dec 17 '09 at 1:11
source share
9 answers

http://blogs.msdn.com/jaimer/archive/2007/03/07/getting-system-dpi-in-wpf-app.aspx seems to work

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; } 
+68
Dec 17 '09 at 1:17
source share
 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); 
+46
Sep 19
source share

With .NET 4.6.2 Preview and higher, you can call VisualTreeHelper.GetDpi(Visual visual) . It returns a DpiScale structure that specifies the DPI in which this Visual file will be displayed.

+18
May 11 '16 at 18:06
source share

The only way I found the โ€œrealโ€ monitor is through the following. All the other methods mentioned just say 96, which is not true for most monitors.

  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, } 
+5
Apr 08 '15 at 13:35 on
source share

I updated my answer from 2015. Here is some utility code that uses the latest DPI features from Windows 10 (in particular, the GetDpiForWindow function, which is the only method that supports DPI_AWARENESS windows / applications / processes, etc.).), But reverts to older ones (dpi on monitor and desktop dpi), so it should still work with Windows 7.

It does not depend on WPF or Winforms, but only on Windows itself.

 // 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, } 
+3
01 Oct '15 at 12:52
source share

This is how I managed to get the "scale factor" in WPF. The resolution of my laptop is 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% 
+2
Feb 28 '19 at 19:03
source share

Use the GetDeviceCaps function:

  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; } 
+1
Sep 14 '18 at 11:58
source share

There is https://blogs.windows.com/buildingapps/2017/01/25/calling-windows-10-apis-desktop-application/#FJtMAIFjbtXiLQAp.97

January 25, 2017 15:54

"Calling the Windows 10 API from a production application" and

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

Provides information about a display monitor device connected to the system.

This data includes frequently used information from the extended identification of the monitor (EDID, which is a standard display descriptor unit that is used by almost all monitors to provide descriptions of supported modes and general device information) and DisplayID (which is a newer industry standard that provides superset of EDID).

raw DPix
Gets the physical horizontal DPI of the monitor (based on the monitorโ€™s native resolution and physical size).

DpiY raw materials
Gets the physical vertical DPI of the monitor (based on the monitorโ€™s native resolution and physical size).

0
Sep 21 '18 at 10:04
source share

Windows monitor basics since 2006

https://docs.microsoft.com/en-us/windows/desktop/wmicoreprov/msmonitorclass

Class MSMonitorClass

Class WmiMonitorRawEEdidV1Block

Class WmiMonitorBasicDisplayParams

 MaxHorizontalImageSize ( EDID byte 21 ) MaxVerticalImageSize ( EDID byte 22 ) 

(Dimensions in EDID are in centimeters above and in millimeters in EDID Detailed time descriptor

12 Horizontal image size, mm, 8 lsbits (0-4095 mm, 161 inches)
13 The vertical size of the image, mm, 8 lsbits (0-4095 mm, 161 inches)
14 bits 7-4 The size of the horizontal image, mm, 4 mb
Bits 3-0 The size of the vertical image, mm, 4 mb

)

as well as

https://social.msdn.microsoft.com/Forums/vstudio/en-US/e7bb9384-b343-4543-ac0f-c98b88a7196f/wpf-wmi-just-get-an-empty-string

0
Sep 22 '18 at 5:38
source share



All Articles