Как получить шкалу DPI для всех экранов?

Мне нужно получить шкалу DPI, установленную на панели управления > Дисплей, для каждого из экранов, подключенных к компьютеру, даже тех, у которых нет окна WPF. Я видел несколько способов получить DPI (например, http://dzimchuk.net/post/Best-way-to-get-DPI-value-in-WPF), но они, похоже, зависят либо от Graphics.FromHwnd(IntPtr.Zero), либо от PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice.

Есть ли способ получить настройки DPI для каждого отдельного экрана?

Фон - я создаю редактор конфигурации макета, чтобы пользователь мог настроить свою конфигурацию до запуска. Для этого я рисую каждый из экранов относительно друг друга. Для одной конфигурации мы используем 4K-дисплей, который имеет больший по умолчанию набор значений DPI. Он намного меньше, чем физически, по отношению к другим экранам, потому что он сообщает о том же разрешении, что и другие экраны.

Ответ 1

Я нашел способ получить dpis с помощью WinAPI. Поскольку сначала нужны ссылки на System.Drawing и System.Windows.Forms. Можно получить ручку монитора с WinAPI от точки в области отображения - класс Screen может дать нам эти точки. Затем функция GetDpiForMonitor возвращает dpi на указанном мониторе.

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,
}

Существует три типа масштабирования, вы можете найти описание в MSDN.

Я быстро протестировал его с помощью нового приложения WPF:

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;
    }
}

Надеюсь, это поможет!