2009-12-17 21 views
46

Làm cách nào để có được DPI trong WPF?Làm cách nào để có được DPI trong WPF?

+1

Tại sao bạn cần phải làm điều đó trong WPF, của tất cả mọi thứ, và bạn sẽ làm gì với giá trị khi bạn nhận được nó? WPF không có cách nào để chỉ định bất kỳ tọa độ nào trong pixel phụ thuộc vào thiết bị. Dường như kích thước của nó bằng pixel, nhưng đó là "pixel ảo" - kích thước của pixel là 96 pixel. Nó sẽ phát triển hoặc thu nhỏ nếu bạn thay đổi hệ thống DPI, do đó, một đường "dày một điểm ảnh" được vẽ bằng cách sử dụng WPF có thể không dày một pixel. –

+1

Vì tôi muốn làm tròn pixel –

+1

Nếu bạn muốn có vị trí chính xác pixel ở ranh giới pixel vật lý, bạn nên sử dụng WPF ngay từ đầu. Nó không phải là kịch bản mà nó được thiết kế, và hoàn toàn không có sự bảo đảm đối với cách các tọa độ WPF có thể được làm tròn vv. Nếu bạn chỉ muốn các đỉnh chụp tới các điểm vật lý gần nhất, sử dụng thuộc tính 'UseLayoutRounding'. Nếu bạn muốn vẽ một đường thẳng chính xác là 10 pixel vật lý, hãy quên đi WPF. –

Trả lời

60

http://blogs.msdn.com/jaimer/archive/2007/03/07/getting-system-dpi-in-wpf-app.aspx dường như làm việc

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

Hãy nhớ rằng các đơn vị WPF không phải là pixel, chúng độc lập với thiết bị @ 96DPI "pixelish-units"; Vì vậy, thực sự những gì bạn muốn, là yếu tố quy mô giữa 96DPI và DPI hiện tại (vì vậy như 1,5 cho 144DPI) –

+0

Vì vậy, đó không phải là những gì tôi cần sau đó :(Làm thế nào để có được các yếu tố quy mô? –

+0

Tôi có nên sử dụng GetDeviceCaps (.., LOGPIXELSX)? –

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

Phương thức này hoạt động ngay cả khi bạn không có tham chiếu đến một điều khiển nhưng nó sử dụng sự phản chiếu để nó có ưu và khuyết điểm của nó nhưng đối với tình huống của tôi, cái này tốt hơn vì tôi không có quyền kiểm soát. –

+2

Phương pháp này có lợi thế là nó hoạt động trước khi sự kiện được tải của một cửa sổ. PresentationSource.FromVisual (myWindow) trả về null cho đến lúc đó. –

+0

Làm việc như một sự quyến rũ. Tôi thích rằng không có giả định trong cách tiếp cận này, không giống như các phiên bản khác với 96 dpi. –

4

Cách duy nhất tôi tìm thấy để có được những "thực" màn dpi là như sau. Tất cả các kỹ thuật được đề cập khác chỉ nói 96 điều không đúng đối với hầu hết các màn hình.

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")] - có nghĩa là chỉ hoạt động cho Windows 8 trở lên – EpiGen

+0

Bạn đã không dán câu lệnh đang sử dụng của mình. Lớp DpiType có trong lớp nào? – rolls

2

Dưới đây là một phương pháp dựa trên công nghệ Direct2D (hỗ trợ trên Windows Vista SP2 và cao hơn và server), vì vậy nó hoạt động tốt trong WPF (mà là dựa trên cơ sở giống nhau). Nó sử dụng số điện thoại 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

Với .NET 4.6.2 Xem trước và cao hơn, bạn có thể gọi VisualTreeHelper.GetDpi(Visual visual). Nó trả về cấu trúc DpiScale, cho bạn biết DPI mà tại đó Visual nhất định sẽ được hoặc đã được trả lại.

+0

Tôi không thể gọi chức năng này, nó xuất hiện dưới dạng không xác định. Bạn có biết tại sao? Tôi đang làm điều này: 'VisualTreeHelper.GetDpi()' –

+0

@AlexRosenfeld đảm bảo rằng bạn đang sử dụng không gian tên System.Windows.Media và phiên bản khung mục tiêu của bạn là 4.6.2 ít nhất.Bạn cũng cần truyền vào một đối tượng kiểu Visual cho api này. – rohit21agrawal

Các vấn đề liên quan