2009-12-17 18 views
46

मैं WPF में डीपीआई कैसे प्राप्त कर सकता हूं?मैं डब्ल्यूपीएफ में डीपीआई कैसे प्राप्त कर सकता हूं?

+1

क्यों आप सब बातों के WPF में यह करने के लिए, की आवश्यकता होगी, और क्या आप एक बार आप इसे पाने के मूल्य के साथ क्या करने जा रहे हैं? डब्ल्यूपीएफ के पास डिवाइस-निर्भर पिक्सल में किसी भी निर्देशांक निर्दिष्ट करने का कोई तरीका नहीं है। ऐसा लगता है कि इसके आकार पिक्सेल में हैं, लेकिन वे "वर्चुअल पिक्सल" हैं - एक पिक्सेल का आकार जैसा कि यह 96 डीपीआई पर है। यदि आप सिस्टम डीपीआई बदलते हैं तो यह बढ़ेगा या सिकुड़ जाएगा, इसलिए WPF का उपयोग करके खींची गई "एक-पिक्सेल मोटी" रेखा शारीरिक रूप से एक-पिक्सेल मोटी नहीं हो सकती है। –

+1

क्योंकि मैं पिक्सेल –

+1

को गोल करना चाहता हूं यदि आप भौतिक पिक्सेल सीमाओं पर पिक्सेल-सटीक प्लेसमेंट चाहते हैं, तो आप पहले स्थान पर WPF का उपयोग न करने से बहुत बेहतर हैं। यह वह परिदृश्य नहीं है जिसके लिए इसे डिज़ाइन किया गया है, और इस बात के संबंध में पूरी तरह से कोई गारंटी नहीं है कि डब्ल्यूपीएफ निर्देशांक कैसे गोल किए जा सकते हैं आदि। यदि आप सिर्फ शीर्ष अक्षरों को निकटतम भौतिक पिक्सेल में स्नैप करना चाहते हैं, तो 'UseLayoutRounding' प्रॉपर्टी' का उपयोग करें। यदि आप एक रेखा को आकर्षित करना चाहते हैं जो बिल्कुल 10 भौतिक पिक्सल लंबा है, तो WPF के बारे में भूल जाओ। –

उत्तर

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 और वर्तमान डीपीआई (ताकि 144DPI के लिए 1.5 की तरह) –

+0

के बीच पैमाने कारक तो है कि क्या मैं तो जरूरत है :(मैं पैमाने कारक? –

+0

मैं 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

यह विधि तब भी काम करती है जब आपके पास कोई संदर्भ नहीं है एक नियंत्रण लेकिन यह प्रतिबिंब का उपयोग करता है, इसलिए इसमें पेशेवर और विपक्ष हैं लेकिन मेरी स्थिति के लिए यह बेहतर था क्योंकि मेरे पास नियंत्रण तक पहुंच नहीं थी। –

+2

इस विधि का लाभ यह है कि यह विंडो के लोड किए गए ईवेंट से पहले काम करता है। प्रस्तुति स्रोत। फ्रॉम विज़ुअल (myWindow) तब तक शून्य देता है। –

+0

एक आकर्षण की तरह काम करता है। मुझे पसंद है कि 96 डीपीआई के साथ अन्य संस्करणों के विपरीत, इस दृष्टिकोण में कोई धारणा नहीं है। –

4

काम करने के लिए मैं "असली" मॉनिटर डीपीआई निम्नलिखित है पाने के लिए मिल गया एक ही रास्ता लगता है। अन्य सभी उल्लिखित तकनीकें केवल 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")] - का मतलब केवल विंडोज 8 और – EpiGen

+0

के लिए काम करता है आपने अपना उपयोग कथन पेस्ट नहीं किया है। डीपीआई टाइप में कौन सी कक्षा निहित है? – rolls

2

यहाँ एक विधि है कि Direct2D प्रौद्योगिकी (SP2 और उच्च और सर्वर के साथ Windows Vista पर समर्थित) पर निर्भर करता है, इसलिए यह 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

का उपयोग करता है के साथ नेट 4.6.2 पूर्वावलोकन और उसके बाद वाले VisualTreeHelper.GetDpi(Visual visual) कॉल कर सकते हैं। यह DpiScale संरचना देता है, जो आपको डीपीआई बताता है जिस पर दिया गया Visual दिया जाएगा या प्रस्तुत किया जाएगा।

+0

मैं इस फ़ंक्शन को कॉल करने में सक्षम नहीं हूं, यह अपरिभाषित के रूप में दिखाई देता है। तुम जानते हो क्यों? मैं यह कर रहा हूं: 'VisualTreeHelper.GetDpi() ' –

+0

@AlexRosenfeld सुनिश्चित करें कि आप नेमस्पेस सिस्टम का उपयोग कर रहे हैं। Windows.Media, और आपका लक्ष्य ढांचा संस्करण 4.6.2 कम से कम है।आपको इस एपीआई के विज़ुअल प्रकार के ऑब्जेक्ट में भी पास करने की आवश्यकता है। – rohit21agrawal

संबंधित मुद्दे