अपने ज्ञात फ़ॉन्ट आकार और वर्णों के लिए WPF TextBlock चौड़ाई की गणना कैसे करें?


81

चलो का कहना है कि मेरे पास है TextBlockपाठ के साथ "कुछ पाठ" और फ़ॉन्ट आकार 10.0

मैं उपयुक्त TextBlock चौड़ाई की गणना कैसे कर सकता हूं ?


यह पहले भी पूछा गया है, यह भी फ़ॉन्ट पर निर्भर है।
एचबी

इसके अलावा, आप सिर्फ वास्तविक चौड़ाई प्राप्त कर सकते हैं ActualWidth
एचबी

2
TextRenderer का उपयोग WPF के लिए भी करना चाहिए: stackoverflow.com/questions/721168/…
HB

जवाबों:


156

FormattedTextकक्षा का उपयोग करें ।

मैंने अपने कोड में एक सहायक कार्य किया:

private Size MeasureString(string candidate)
{
    var formattedText = new FormattedText(
        candidate,
        CultureInfo.CurrentCulture,
        FlowDirection.LeftToRight,
        new Typeface(this.textBlock.FontFamily, this.textBlock.FontStyle, this.textBlock.FontWeight, this.textBlock.FontStretch),
        this.textBlock.FontSize,
        Brushes.Black,
        new NumberSubstitution(),
        1);

    return new Size(formattedText.Width, formattedText.Height);
}

यह डिवाइस-स्वतंत्र पिक्सेल लौटाता है जिसका उपयोग WPF लेआउट में किया जा सकता है।


यह वास्तव में मददगार था
राकेश

1
UWP में ऐसा कैसे करें
अरुण प्रसाद

6
@ अरुनप्रसाद एक अलग सवाल पर एक उत्कृष्ट बात होगी। यह प्रश्न स्पष्ट रूप से WPF के लिए है।
रैंडम एनी

यदि उम्मीदवार पास हुआ तो यह शून्य की चौड़ाई है। मुझे लगता है कि यह संभवतः रैपिंग शब्द के साथ करना है?
मार्क मिलर

43

रिकॉर्ड के लिए ... मुझे लगता है कि op'er क्रमिक रूप से उस चौड़ाई को निर्धारित करने की कोशिश कर रहा है जिसे टेक्स्टब्लॉक दृश्य पेड़ में जोड़े जाने के बाद उठाएगा। IMO एक बेहतर समाधान तो स्वरूपित पाठ (आप टेक्स्टव्राफिंग जैसी किसी चीज़ को कैसे संभालते हैं?) एक नमूना टेक्स्टब्लॉक पर माप और व्यवस्था का उपयोग करना होगा। जैसे

var textBlock = new TextBlock { Text = "abc abd adfdfd", TextWrapping = TextWrapping.Wrap };
// auto sized
textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
textBlock.Arrange(new Rect(textBlock.DesiredSize));

Debug.WriteLine(textBlock.ActualWidth); // prints 80.323333333333
Debug.WriteLine(textBlock.ActualHeight);// prints 15.96

// constrain the width to 16
textBlock.Measure(new Size(16, Double.PositiveInfinity));
textBlock.Arrange(new Rect(textBlock.DesiredSize));

Debug.WriteLine(textBlock.ActualWidth); // prints 14.58
Debug.WriteLine(textBlock.ActualHeight);// prints 111.72

1
कुछ मामूली संशोधनों के साथ, इसने मेरे लिए विंडोज 8.1 स्टोर ऐप (विंडोज़ रनटाइम) लिखने का काम किया। इस TextBlock के लिए फ़ॉन्ट जानकारी सेट करना न भूलें ताकि यह चौड़ाई की सही गणना कर सके। नीट समाधान और एक उत्थान के लायक है।
डेविड रेक्टर

1
यहां मुख्य बिंदु यह अस्थायी टेक्स्टब्लॉक बना रहा है। एक पृष्ठ पर मौजूदा टेक्स्टब्लॉक के साथ ये सभी जोड़तोड़ काम नहीं करते हैं: इसके एक्चुअरीविथ को केवल redraw के बाद अपडेट किया जाता है।
टर्शियम

13

प्रदान किया गया समाधान .Net फ्रेमवर्क 4.5 के लिए उपयुक्त था, हालाँकि, विंडोज 10 डीपीआई स्केलिंग और फ्रेमवर्क 4.6.x के लिए इसके लिए अलग-अलग समर्थन डिग्री है, पाठ को मापने के लिए उपयोग किया जाने वाला निर्माण अब चिह्नित है [Obsolete], साथ ही उस पद्धति पर किसी भी निर्माणकर्ता के साथ। pixelsPerDipपैरामीटर शामिल नहीं है ।

दुर्भाग्य से, यह थोड़ा अधिक शामिल है, लेकिन यह नई स्केलिंग क्षमताओं के साथ अधिक सटीकता का परिणाम देगा।

### PixelsPerDip

MSDN के अनुसार, यह प्रतिनिधित्व करता है:

पिक्सेल प्रति घनत्व स्वतंत्र पिक्सेल मान, जो स्केल फैक्टर के बराबर है। उदाहरण के लिए, यदि किसी स्क्रीन की DPI 120 (या 1.25 क्योंकि 120/96 = 1.25) है, तो 1.25 पिक्सेल प्रति घनत्व स्वतंत्र पिक्सेल खींची जाती है। DIP माप की इकाई है जिसका उपयोग डिवाइस रिज़ॉल्यूशन और DPI से स्वतंत्र होने के लिए WPF द्वारा किया जाता है।

यहाँ Microsoft / WPF- नमूने GitHub रिपॉजिटरी से DPI स्केलिंग जागरूकता के मार्गदर्शन के आधार पर चयनित उत्तर का मेरा कार्यान्वयन है ।

पूरी तरह से विंडोज 10 एनिवर्सरी (कोड के नीचे) के रूप में डीपीआई स्केलिंग का समर्थन करने के लिए कुछ अतिरिक्त कॉन्फ़िगरेशन की आवश्यकता होती है , जिसे मैं काम नहीं कर सकता था, लेकिन इसके बिना यह एकल मॉनिटर पर स्केलिंग कॉन्फ़िगर (और स्केलिंग परिवर्तनों का सम्मान करता है) के साथ काम करता है। उपरोक्त रेपो में वर्ड डॉक्यूमेंट उस सूचना का स्रोत है क्योंकि जब मैंने उन मानों को जोड़ा तो मेरा आवेदन लॉन्च नहीं होगा। उसी रेपो से इस सैंपल कोड को एक अच्छे संदर्भ बिंदु के रूप में भी काम किया जाता है।

public partial class MainWindow : Window
{
    private DpiScale m_dpiInfo;
    private readonly object m_sync = new object();

    public MainWindow()
    {
        InitializeComponent();
        Loaded += OnLoaded;
    }
    
    private Size MeasureString(string candidate)
    {
        DpiScale dpiInfo;
        lock (m_dpiInfo)
            dpiInfo = m_dpiInfo;

        if (dpiInfo == null)
            throw new InvalidOperationException("Window must be loaded before calling MeasureString");

        var formattedText = new FormattedText(candidate, CultureInfo.CurrentUICulture,
                                              FlowDirection.LeftToRight,
                                              new Typeface(this.textBlock.FontFamily, 
                                                           this.textBlock.FontStyle, 
                                                           this.textBlock.FontWeight, 
                                                           this.textBlock.FontStretch),
                                              this.textBlock.FontSize,
                                              Brushes.Black, 
                                              dpiInfo.PixelsPerDip);
        
        return new Size(formattedText.Width, formattedText.Height);
    }

// ... The Rest of Your Class ...

    /*
     * Event Handlers to get initial DPI information and to set new DPI information
     * when the window moves to a new display or DPI settings get changed
     */
    private void OnLoaded(object sender, RoutedEventArgs e)
    {            
        lock (m_sync)
            m_dpiInfo = VisualTreeHelper.GetDpi(this);
    }

    protected override void OnDpiChanged(DpiScale oldDpiScaleInfo, DpiScale newDpiScaleInfo)
    {
        lock (m_sync)
            m_dpiInfo = newDpiScaleInfo;

        // Probably also a good place to re-draw things that need to scale
    }
}

अन्य आवश्यकताएं

Microsoft / WPF- नमूने पर प्रलेखन के अनुसार, आपको कई मॉनिटर मॉनिटरिंग में अलग-अलग DPI सेटिंग्स प्रति प्रदर्शन में Windows 10 वर्षगांठ की क्षमता को कवर करने के लिए एप्लिकेशन के प्रकटन में कुछ सेटिंग्स जोड़ने की आवश्यकता है। यह एक उचित अनुमान है कि इन सेटिंग्स के बिना, OnDpiChanged घटना को तब नहीं उठाया जा सकता है जब एक विंडो को एक डिस्प्ले से दूसरे में अलग-अलग सेटिंग्स के साथ स्थानांतरित किया जाता है, जो आपके माप को पिछले पर भरोसा करना जारी रखेगा DpiScale। जो एप्लिकेशन मैं लिख रहा था, वह मेरे लिए था, अकेला था, और मेरे पास उस तरह का सेटअप नहीं था, इसलिए मेरे पास परीक्षण करने के लिए कुछ भी नहीं था और जब मैंने मार्गदर्शन का पालन किया, तो मैं एक ऐप के साथ समाप्त हुआ जो प्रकट होने के कारण शुरू नहीं होगा। त्रुटियां, इसलिए मैंने हार मान ली, लेकिन उस ऐप को प्रकट करने के लिए इसे देखना और समायोजित करना एक अच्छा विचार होगा:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
    </windowsSettings>
</application>

प्रलेखन के अनुसार:

[इन] दो टैगों के संयोजन का निम्नलिखित प्रभाव है: 1) प्रति-मॉनिटर के लिए> = विंडोज 10 वर्षगांठ अपडेट 2) प्रणाली <विंडोज 10 वर्षगांठ अपडेट


5

मुझे कुछ तरीके मिले जो ठीक काम करते हैं ...

/// <summary>
/// Get the required height and width of the specified text. Uses Glyph's
/// </summary>
public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
    Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
    GlyphTypeface glyphTypeface;

    if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
    {
        return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize);
    }

    double totalWidth = 0;
    double height = 0;

    for (int n = 0; n < text.Length; n++)
    {
        ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];

        double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize;

        double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize;

        if (glyphHeight > height)
        {
            height = glyphHeight;
        }

        totalWidth += width;
    }

    return new Size(totalWidth, height);
}

/// <summary>
/// Get the required height and width of the specified text. Uses FortammedText
/// </summary>
public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
    FormattedText ft = new FormattedText(text,
                                            CultureInfo.CurrentCulture,
                                            FlowDirection.LeftToRight,
                                            new Typeface(fontFamily, fontStyle, fontWeight, fontStretch),
                                            fontSize,
                                            Brushes.Black);
    return new Size(ft.Width, ft.Height);
}

3
अधिकांश फोंट के लिए सुमिंग ग्लिफ़ चौड़ाई काम नहीं करेगी क्योंकि यह कर्निंग के लिए खाता नहीं है ।
निकोलस रिपब्लिक

4

मैंने बैकएंड कोड में तत्व के लिए एक बाध्यकारी पथ जोड़कर इसे हल किया:

<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />

मैंने इसे अपने कोड में फ़ॉर्मटेडटेक्स्ट जैसे उपरोक्त संदर्भों के सभी ओवरहेड को जोड़ने की तुलना में बहुत अधिक क्लीनर समाधान पाया।

के बाद, मैं यह करने में सक्षम था:

double d_width = MyText.Width;

2
आप बस d_width = MyText.ActualWidth;बंधन के बिना कर सकते हैं । समस्या तब है जब TextBlockदृश्य पेड़ में अभी तक नहीं है।
xmedeko

0

मैं इस एक का उपयोग करें:

var typeface = new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch);
var formattedText = new FormattedText(textBlock.Text, Thread.CurrentThread.CurrentCulture, textBlock.FlowDirection, typeface, textBlock.FontSize, textBlock.Foreground);

var size = new Size(formattedText.Width, formattedText.Height)

-3

आपके लिए यह मिला:

Graphics g = control.CreateGraphics();
int width =(int)g.MeasureString(aString, control.Font).Width; 
g.dispose();

24
यह विशिष्ट दृष्टिकोण केवल WinForms पर लागू होता है।
एचबी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.