मैं एक WPF आकार को भौतिक पिक्सल में कैसे परिवर्तित करूं?




dpi elementhost (3)

मुझे ऐसा करने का एक तरीका मिला, लेकिन मुझे यह पसंद नहीं है:

using (var graphics = Graphics.FromHwnd(IntPtr.Zero))
{
    var pixelWidth = (int) (element.DesiredSize.Width * graphics.DpiX / 96.0);
    var pixelHeight = (int) (element.DesiredSize.Height * graphics.DpiY / 96.0);
    // ...
}

मुझे यह पसंद नहीं है क्योंकि (ए) इसे सिस्टम के संदर्भ की आवश्यकता है। डब्ल्यूपीएफ एपीआई का उपयोग करने के बजाय, ड्रॉइंग; और (बी) मुझे गणित स्वयं करना है, जिसका अर्थ है कि मैं डब्ल्यूपीएफ के कार्यान्वयन विवरणों को डुप्लिकेट कर रहा हूं। .NET 3.5 में, मुझे ElementHost AutoSize = true के साथ क्या मेल खाता है, उससे मेल खाने के लिए गणना के परिणाम को छोटा करना है, लेकिन मुझे नहीं पता कि यह अभी भी .NET के भविष्य के संस्करणों में सटीक होगा या नहीं।

ऐसा लगता है कि यह काम करता है, इसलिए अगर मैं दूसरों की मदद करता हूं तो मैं इसे पोस्ट कर रहा हूं। लेकिन अगर किसी के पास बेहतर जवाब है, तो कृपया पोस्ट करें।

एक WPF (रिज़ॉल्यूशन-स्वतंत्र) चौड़ाई और ऊंचाई को भौतिक स्क्रीन पिक्सल में परिवर्तित करने का सबसे अच्छा तरीका क्या है?

मैं WinForms प्रपत्र (ElementHost के माध्यम से) में WPF सामग्री दिखा रहा हूं और कुछ आकार देने वाले तर्क को काम करने का प्रयास कर रहा हूं। जब यह डिफ़ॉल्ट 96 डीपीआई पर चल रहा है तो मुझे यह ठीक काम मिल गया है। लेकिन यह तब काम नहीं करेगा जब ओएस 120 डीपीआई या कुछ अन्य रिज़ॉल्यूशन पर सेट हो, क्योंकि तब एक डब्ल्यूपीएफ तत्व जो इसकी चौड़ाई को 96 के रूप में रिपोर्ट करता है, वास्तव में WinForms के संबंध में 120 पिक्सल चौड़ा होगा।

मुझे System.Windows.SystemParameters पर कोई भी "पिक्सल प्रति इंच" सेटिंग्स नहीं मिल सका। मुझे यकीन है कि मैं WinForms समकक्ष (System.Windows.Forms.SystemInformation) का उपयोग कर सकता हूं, लेकिन ऐसा करने का एक बेहतर तरीका है (पढ़ें: WinForms API का उपयोग करने और मैन्युअल रूप से गणित करने के बजाय WPF API का उपयोग करने का एक तरीका)? डब्ल्यूपीएफ "पिक्सल" को वास्तविक स्क्रीन पिक्सेल में बदलने के लिए "सबसे अच्छा तरीका" क्या है?

संपादित करें: मैं स्क्रीन पर डब्ल्यूपीएफ नियंत्रण दिखाए जाने से पहले भी ऐसा करने की कोशिश कर रहा हूं। ऐसा लगता है कि मुझे सही जवाब देने के लिए Visual.PointToScreen बनाया जा सकता है, लेकिन मैं इसका उपयोग नहीं कर सकता, क्योंकि नियंत्रण अभी तक अभिभावित नहीं है और मुझे अवैधऑपरेशन अपवाद मिलता है "यह दृश्य प्रस्तुति स्रोत से कनेक्ट नहीं है"।


Screen.WorkingArea और SystemParameters.WorkArea बीच सरल अनुपात:

private double PointsToPixels (double wpfPoints, LengthDirection direction)
{
    if (direction == LengthDirection.Horizontal)
    {
        return wpfPoints * Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.WorkArea.Width;
    }
    else
    {
        return wpfPoints * Screen.PrimaryScreen.WorkingArea.Height / SystemParameters.WorkArea.Height;
    }
}

private double PixelsToPoints(int pixels, LengthDirection direction)
{
    if (direction == LengthDirection.Horizontal)
    {
        return pixels * SystemParameters.WorkArea.Width / Screen.PrimaryScreen.WorkingArea.Width;
    }
    else
    {
        return pixels * SystemParameters.WorkArea.Height / Screen.PrimaryScreen.WorkingArea.Height;
    }
}

public enum LengthDirection
{
    Vertical, // |
    Horizontal // ——
}

यह कई मॉनीटर के साथ भी ठीक काम करता है।


एक ज्ञात आकार को डिवाइस पिक्सेल में बदलना

यदि आपका विज़ुअल तत्व पहले से प्रस्तुति स्रोत से जुड़ा हुआ है (उदाहरण के लिए, यह स्क्रीन पर दिखाई देने वाली विंडो का हिस्सा है), तो ट्रांसफॉर्म इस तरह से मिलता है:

var source = PresentationSource.FromVisual(element);
Matrix transformToDevice = source.CompositionTarget.TransformToDevice;

यदि नहीं, तो एक अस्थायी hWnd बनाने के लिए HwndSource का उपयोग करें:

Matrix transformToDevice;
using(var source = new HwndSource(new HwndSourceParameters()))
  transformToDevice = source.TransformToDevice;

ध्यान दें कि IntPtr.Zero के HWnd का उपयोग करके निर्माण करने से यह कम कुशल है लेकिन मैं इसे अधिक विश्वसनीय मानता हूं क्योंकि HwndSource द्वारा बनाए गए एचडब्ल्यूएनडी को एक ही डिस्प्ले डिवाइस से जोड़ा जाएगा जो वास्तविक नव निर्मित विंडो के रूप में होगा। इस तरह, यदि अलग-अलग डिस्प्ले डिवाइसों में अलग-अलग डीपीआई होते हैं तो आपको सही डीपीआई मान प्राप्त करना सुनिश्चित होता है।

एक बार आपके पास ट्रांसफॉर्म हो जाने के बाद, आप किसी भी आकार को WPF आकार से पिक्सेल आकार में परिवर्तित कर सकते हैं:

var pixelSize = (Size)transformToDevice.Transform((Vector)wpfSize);

पिक्सेल आकार को पूर्णांक में कनवर्ट करना

यदि आप पिक्सेल आकार को पूर्णांक में कनवर्ट करना चाहते हैं, तो आप बस ऐसा कर सकते हैं:

int pixelWidth = (int)pixelSize.Width;
int pixelHeight = (int)pixelSize.Height;

लेकिन ElementHost द्वारा उपयोग किया जाने वाला एक और मजबूत समाधान होगा:

int pixelWidth = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Width));
int pixelHeight = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Height));

UIElement के वांछित आकार प्राप्त करना

UIElement के वांछित आकार को प्राप्त करने के लिए आपको यह सुनिश्चित करने की आवश्यकता है कि इसे मापा जाए। कुछ परिस्थितियों में यह पहले से ही मापा जाएगा, या तो क्योंकि:

  1. आपने इसे पहले ही मापा है
  2. आपने अपने पूर्वजों में से एक को मापा है, या
  3. यह प्रस्तुति स्रोत का हिस्सा है (उदाहरण के लिए यह एक दृश्यमान विंडो में है) और आप नीचे डिस्पैचर प्राथमिकता को निष्पादित कर रहे हैं। प्रस्तुत करें ताकि आप जान सकें कि माप स्वचालित रूप से पहले से ही हो चुका है।

यदि आपका विज़ुअल तत्व अभी तक मापा नहीं गया है, तो आपको उपलब्ध आकार में गुजरने के लिए माप या उसके पूर्वजों में से एक को उचित आकार में गुजरना चाहिए (या new Size(double.PositivieInfinity, double.PositiveInfinity) यदि आप सामग्री का आकार बनाना चाहते हैं :

element.Measure(availableSize);

एक बार मापने के बाद, वांछित आकार बदलने के लिए मैट्रिक्स का उपयोग करना आवश्यक है:

var pixelSize = (Size)transformToDevice.Transform((Vector)element.DesiredSize);

यह सब एक साथ डालें

यहां एक सरल विधि है जो दिखाती है कि किसी तत्व के पिक्सेल आकार को कैसे प्राप्त करें:

public Size GetElementPixelSize(UIElement element)
{
  Matrix transformToDevice;
  var source = PresentationSource.FromVisual(element);
  if(source!=null)
    transformToDevice = source.CompositionTarget.TransformToDevice;
  else
    using(var source = new HwndSource(new HwndSourceParameters()))
      transformToDevice = source.CompositionTarget.TransformToDevice;

  if(element.DesiredSize == new Size())
    element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

  return (Size)transformToDevice.Transform((Vector)element.DesiredSize);
}

ध्यान दें कि इस कोड में मैं केवल मापन को कॉल करता हूं यदि कोई वांछित आकार मौजूद नहीं है। यह सबकुछ करने के लिए एक सुविधाजनक तरीका प्रदान करता है लेकिन इसमें कई कमीएं हैं:

  1. ऐसा हो सकता है कि तत्व का अभिभावक एक छोटे से उपलब्ध आकार में पारित होता
  2. वास्तविक वांछित आकार शून्य होने पर यह अक्षम है (इसे बार-बार पुनः प्राप्त किया जाता है)
  3. यह बग को मास्क कर सकता है जिससे अप्रत्याशित समय के कारण एप्लिकेशन विफल हो जाता है (उदाहरण के लिए। DispatchPriority.Render पर या उसके बाद कोड को कॉल किया जा रहा है)

इन कारणों से, मैं GetElementPixelSize में माप कॉल को छोड़ने के इच्छुक हूं और बस ग्राहक को ऐसा करने दें।





elementhost