2009-12-17 49 views
46

如何获得WPF中的DPI?如何获得WPF中的DPI?

+1

为什么你需要在WPF中做所有的事情,一旦你得到它,你将如何处理这个值? WPF无法指定设备相关像素中的任何坐标。看起来它的大小是以像素为单位的,但它们是“虚拟像素” - 像素大小为96 DPI。如果您更改系统DPI,它将会增大或缩小,因此使用WPF绘制的“一个像素厚度”线可能实际上不是一个像素厚。 – 2009-12-17 01:20:41

+1

因为我想四舍五入像素 – 2009-12-17 01:43:55

+1

如果你想在物理像素边界处进行像素精确放置,那么最好不要使用WPF。这不是它所设计的场景,并且关于WPF坐标如何舍入等方面绝对没有保证。如果您只想将顶点捕捉到最近的物理像素,请使用“UseLayoutRounding”属性。如果你想绘制一条长度恰好为10个物理像素的线条,那么忘记WPF。 – 2009-12-17 01:51:06

回答

60

http://blogs.msdn.com/jaimer/archive/2007/03/07/getting-system-dpi-in-wpf-app.aspx似乎工作

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

请记住,尽管WPF单元不是像素,但它们是设备无关的@ 96DPI“像素单位”;所以你真正想要的是96DPI和当前DPI之间的比例因子(对于144DPI而言就是1.5) – 2009-12-17 01:51:40

+0

所以这不是我需要的那么:(我如何得到比例因子? – 2009-12-17 18:02:44

+0

我应该使用GetDeviceCaps(.., LOGPIXELSX)? – 2009-12-17 18:10:54

35
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); 
+1

即使您没有对一个控件,但它确实使用了反射,所以它具有优点和缺点,但对于我的情况来说,这一个更好,因为我没有控制权。 – 2013-08-01 18:47:29

+2

此方法的优点是它在窗口的Loaded事件之前工作。直到那时,PresentationSource.FromVisual(myWindow)才会返回null。 – 2013-10-16 22:18:49

+0

工程就像一个魅力。我喜欢这种方法没有任何假设,不像96 dpi的其他版本。 – 2015-08-20 02:07:29

4

我发现得到了 “真正的” 监控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, 
} 
+2

[DllImport(“Shcore.dll”)] - 表示仅适用于Windows 8及更高版本 – EpiGen 2015-08-17 08:11:08

+0

您没有粘贴您的使用语句。 DpiType包含哪些类? – rolls 2017-08-10 01:37:15

2

下面是依赖于技术的Direct2D(支持Windows Vista带有SP2和更高服务器)的方法,所以它在WPF(它是基于同样的理由)工作正常。它使用ID2D1Factory::GetDesktopDpi method

public static class DpiUtilities 
    { 
     private static Lazy<Tuple<float, float>> _dpi = new Lazy<Tuple<float, float>>(ReadDpi); 

     public static float DesktopDpiX 
     { 
      get 
      { 
       return _dpi.Value.Item1; 
      } 
     } 

     public static float DesktopDpiY 
     { 
      get 
      { 
       return _dpi.Value.Item2; 
      } 
     } 

     public static void Reload() 
     { 
      ID2D1Factory factory; 
      int hr = D2D1CreateFactory(D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED, typeof(ID2D1Factory).GUID, IntPtr.Zero, out factory); 
      if (hr != 0) 
       Marshal.ThrowExceptionForHR(hr); 

      factory.ReloadSystemMetrics(); 
      Marshal.ReleaseComObject(factory); 
     } 

     private static Tuple<float, float> ReadDpi() 
     { 
      ID2D1Factory factory; 
      int hr = D2D1CreateFactory(D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED, typeof(ID2D1Factory).GUID, IntPtr.Zero, out factory); 
      if (hr != 0) 
       Marshal.ThrowExceptionForHR(hr); 

      float x; 
      float y; 
      factory.GetDesktopDpi(out x, out y); 
      Marshal.ReleaseComObject(factory); 
      return new Tuple<float, float>(x, y); 
     } 

     [DllImport("d2d1.dll")] 
     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 
     } 
    } 
8

在.NET 4.6.2预览和更高的,你可以调用VisualTreeHelper.GetDpi(Visual visual)。它返回一个DpiScale结构,它告诉你给定的Visual将被呈现或者已经被呈现的DPI。

+0

我无法调用此函数,它显示为未定义。你知道为什么吗?我这样做:'VisualTreeHelper.GetDpi()' – 2017-03-29 19:23:04

+0

@AlexRosenfeld确保您使用的是命名空间System.Windows.Media,并且您的目标框架版本是至少4.6.2。您还需要将Visual类型的对象传递给此api。 – rohit21agrawal 2017-03-30 19:37:57