2009-12-17 6 views
46

Wie kann ich die DPI in WPF bekommen?Wie kann ich die DPI in WPF bekommen?

+1

Warum sollten Sie es ausgerechnet in WPF machen, und was werden Sie mit dem Wert machen, sobald Sie es bekommen haben? WPF hat keine Möglichkeit, in geräteabhängigen Pixeln Koordinaten anzugeben. Es kann scheinen, dass seine Größen in Pixeln sind, aber das sind "virtuelle Pixel" - die Größe eines Pixels wie bei 96 DPI. Wenn Sie das System-DPI ändern, wird es größer oder kleiner, so dass eine "ein Pixel dicke" Linie, die mit WPF gezeichnet wurde, physikalisch möglicherweise nicht ein Pixel dick ist. –

+1

Weil ich Pixel runden möchte –

+1

Wenn Sie eine pixelgenaue Platzierung an physischen Pixelbegrenzungen wünschen, ist es viel besser, WPF überhaupt nicht zu verwenden. Es ist nicht das Szenario, für das es entworfen wurde, und es gibt absolut keine Garantien in Bezug darauf, wie WPF-Koordinaten abgerundet werden können usw. Wenn Sie nur die Scheitelpunkte an den nächsten physischen Pixeln ausrichten möchten, verwenden Sie die Eigenschaft 'UseLayoutRounding'. Wenn Sie eine Linie zeichnen möchten, die genau 10 physikalische Pixel lang ist, dann vergessen Sie WPF. –

Antwort

60

http://blogs.msdn.com/jaimer/archive/2007/03/07/getting-system-dpi-in-wpf-app.aspx scheint, dass ich die "echte" Monitor dpi wird wie folgt erhalten gefunden

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

Beachten Sie jedoch, dass WPF-Einheiten keine Pixel sind, sie sind geräteunabhängig @ 96DPI "pixelish-units"; Also wirklich was du willst, ist der Skalierungsfaktor zwischen 96DPI und der aktuellen DPI (also wie bei 1.5 für 144DPI) –

+0

Also das ist nicht was ich dann brauche :(Wie bekomme ich den Skalierungsfaktor? –

+0

Soll ich 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

Diese Methode funktioniert, auch wenn Sie keine Referenz zu haben eine Kontrolle, aber es verwendet Reflexion, so hat es seine Vor- und Nachteile, aber für meine Situation war diese besser, da ich keinen Zugang zu einer Kontrolle hatte. –

+2

Diese Methode hat den Vorteil, dass sie vor dem Loaded-Ereignis eines Fensters funktioniert. PresentationSource.FromVisual (myWindow) gibt bis dahin null zurück. –

+0

Funktioniert wie ein Charme. Ich mag, dass es im Gegensatz zu anderen Versionen mit 96 dpi keine Annahmen in diesem Ansatz gibt. –

4

Der einzige Weg, zu arbeiten. Alle anderen erwähnten Techniken sagen nur 96, was für die meisten Monitore nicht korrekt ist.

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")] - bedeutet funktioniert nur für Windows 8 und höher – EpiGen

+0

Sie haben Ihre using-Anweisung nicht eingefügt. In welcher Klasse ist DpiType enthalten? – rolls

2

Hier ist eine Methode, die auf der Direct2D-Technologie (unterstützt unter Windows Vista mit SP2 und höher und Server) angewiesen ist, so funktioniert es in WPF in Ordnung (die auf denselben Gründen beruht). Es nutzt die 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

Mit .NET 4.6.2 Preview und höher, können Sie VisualTreeHelper.GetDpi(Visual visual) nennen. Es gibt eine DpiScale Struktur zurück, die Ihnen die DPI mitteilt, bei der Visual ausgegeben wird oder wurde.

+0

Ich kann diese Funktion nicht aufrufen, sie erscheint als undefiniert. Weißt du, warum? Ich mache das: 'VisualTreeHelper.GetDpi()' –

+0

@AlexRosenfeld stellen Sie sicher, dass Sie den Namespace System.Windows.Media verwenden, und Ihre Ziel-Framework-Version ist 4.6.2 atleast.Sie müssen auch ein Objekt vom Typ Visual an diese API übergeben. – rohit21agrawal

Verwandte Themen