23

Using WPF, what is the most efficient way to measure a large number of short strings? Specifically, I'd like to determine the display height of each string, given uniform formatting (same font, size, weight, etc.) and the maximum width the string may occupy?

Tony the Pony
  • 40,327
  • 71
  • 187
  • 281
  • 1
    Same question on MSDN forums got some better solutions: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/a450b0aa-7086-4145-9eff-a779571b6187/ – srgstm Oct 02 '11 at 05:55

4 Answers4

14

The most low-level technique (and therefore giving the most scope for creative optimisations) is to use GlyphRuns.

It's not very well documented but I wrote up a little example here:

http://smellegantcode.wordpress.com/2008/07/03/glyphrun-and-so-forth/

The example works out the length of the string as a necessary step before rendering it.

Jordan S. Jones
  • 13,703
  • 5
  • 44
  • 49
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 1
    It seems the example doesn't take into account more complex concepts like kernings and standard ligatures, Daniel, could you confirm (10 years later :) )? – Borislav Ivanov Aug 07 '19 at 05:34
7

In WPF:

Remember to call Measure() on the TextBlock before reading the DesiredSize property.

If the TextBlock was created on-the-fly, and not yet shown, you have to call Measure() first, like so:

MyTextBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

return new Size(MyTextBlock.DesiredSize.Width, MyTextBlock.DesiredSize.Height);

In Silverlight:

No need to measure.

return new Size(TextBlock.ActualWidth, TextBlock.ActualHeight);

The complete code looks like this:

public Size MeasureString(string s) {

    if (string.IsNullOrEmpty(s)) {
        return new Size(0, 0);
    }

    var TextBlock = new TextBlock() {
        Text = s
    };

#if SILVERLIGHT
    return new Size(TextBlock.ActualWidth, TextBlock.ActualHeight);
#else
    TextBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

    return new Size(TextBlock.DesiredSize.Width, TextBlock.DesiredSize.Height);
#endif
}
Martin Lottering
  • 1,624
  • 19
  • 31
6

It is very simple and done by FormattedText class! Try it.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Mahdi Nameghi
  • 69
  • 2
  • 7
3

You can use the DesiredSize property on a rendered TextBox to get the height and width

using System.Windows.Threading;

...

Double TextWidth = 0;
Double TextHeight = 0;
...

MyTextBox.Text = "Words to measure size of";
this.Dispatcher.BeginInvoke(
    DispatcherPriority.Background,
    new DispatcherOperationCallback(delegate(Object state) {
        var size = MyTextBox.DesiredSize;
        this.TextWidth = size.Width;
        this.TextHeight = size.Height;
        return null; 
    }
) , null);

If you have a large number of strings it may be quicker to first pre-calualte the height and width of every indiviudal letter and symbol in a given font, and then do a calculation based on the string chars. This may not be 100% acurate due to kerning etc

TFD
  • 23,890
  • 2
  • 34
  • 51