Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.7k views
in Technique[技术] by (71.8m points)

c# - How to get DPI scale for all screens?

I need to get the DPI scale, as set from Control Panel > Display, for each of the screens connected to the computer, even those that do not have a WPF window open. I have seen a number of ways to get DPI (for example, http://dzimchuk.net/post/Best-way-to-get-DPI-value-in-WPF) but these seem to be dependent on either Graphics.FromHwnd(IntPtr.Zero) or PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice.

Is there a way to get the DPI settings for each individual screen?

Background - I am creating a layout configuration editor so that the user can set up their configuration prior to launch. For this, I draw each of the screens relative to each other. For one configuration we are using a 4K display that has a larger than default DPI scale set. It is drawing much smaller than it physically appears in relation to the other screens because it reports as the same resolution as the other screens.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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
");
    sb.Append(string.Join("
", Display(DpiType.Angular)));
    sb.Append("
Effective
");
    sb.Append(string.Join("
", Display(DpiType.Effective)));
    sb.Append("
Raw
");
    sb.Append(string.Join("
", 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!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...