How to Get Dpi Scale for All Screens

How to get DPI scale for all screens?

I found a way to get the dpi’s with the WinAPI.
As first needs references to System.Drawing and System.Windows.Forms. It is possible to get the monitor handle with the WinAPI from a point on the display area - the Screen class can give us this points. Then the GetDpiForMonitor function returns the dpi of the specified monitor.

public static class ScreenExtensions
{
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}

//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).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(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
}

//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}

There are three types of scaling, you can find a description in the MSDN.

I tested it quickly with a new WPF application:

private void Window_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var sb = new StringBuilder();
sb.Append("Angular\n");
sb.Append(string.Join("\n", Display(DpiType.Angular)));
sb.Append("\nEffective\n");
sb.Append(string.Join("\n", Display(DpiType.Effective)));
sb.Append("\nRaw\n");
sb.Append(string.Join("\n", Display(DpiType.Raw)));

this.Content = new TextBox() { Text = sb.ToString() };
}

private IEnumerable<string> Display(DpiType type)
{
foreach (var screen in System.Windows.Forms.Screen.AllScreens)
{
uint x, y;
screen.GetDpi(type, out x, out y);
yield return screen.DeviceName + " - dpiX=" + x + ", dpiY=" + y;
}
}

I hope it helps!

How to get scaling factor for each monitor, e.g. 1, 1.25, 1.5

I believe I have finally (after a long time of searching) found an answer that works, it even works on my high DPI Surface Book 2 screen. I have tested it as much as I can, and so far it's always returned the correct value.

Here's how I did it, thanks to whoever posted the code fragments in the past where I gathered this from.

First you need a structure to call EnumDisplaySettings in user32.dll

    [StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 0x20;
private const int CCHFORMNAME = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public ScreenOrientation dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}

Then you need to declare the external function call

[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);

Then you need to use it to calculate the screen scaling

            Screen[] screenList = Screen.AllScreens;

foreach (Screen screen in screenList)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
EnumDisplaySettings(screen.DeviceName, -1, ref dm);

var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
}

Hope others find this useful.

C# Getting DPI Scaling for each Monitor in Windows

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.

You may also want to check out the VisualTreeHelper.GetDpi method.

Get scale of screen

Most methods to retrieve DPI depends on existing controls, which may be inconvenient sometimes. But DPI can be always retrieved from registry. In C#:

using Microsoft.Win32;
//...
var currentDPI = (int)Registry.GetValue("HKEY_CURRENT_USER\\Control Panel\\Desktop", "LogPixels", 96);

Scale will be

var scale = 96/(float)currentDPI;


Related Topics



Leave a reply



Submit