Search code examples
wpffontstextblock

Counting font size asynchronously


I use WPF. My app has some textblocks with changeable text inside. Each one has width of 200 and height of 150. The problem is that I have 7 textblocks like those and I want them to have the same font size. The text must be autofitted. I know that can autofit them up. But when one has a sentence inside and another only two words, the font size is so different... I need to recount size asynchronically (e.g. creating some event like OnTextChange). Text inside blocks changes dynamically. How to write a function? I want to pass 3 parameters: text, font (font family + font style) and textblock size, and return fitted font size.


Solution

  • The best way to determine the appropriate font size is to measure the text at any arbitrary size, and then multiply it by the ratio of its size to the size of the area.

    For example, if you measure the text and it is half of the size of the container it's in, you can multiply it by 2 and it should fill the container. You want to choose the minimum of either the width or height ratio to use.

    In WPF the FormattedText class does text measuring.

    public double GetFontSize(string text, Size availableSize, Typeface typeFace)
    {
        FormattedText formtxt = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 10, Brushes.Black);
    
        double ratio = Math.Min(availableSize.Width / formtxt.Width, availableSize.Height / formtxt.Height);
    
        return 10 * ratio;
    }
    

    You would use this function whenever you change the text of your TextBlocks, like so:

    txtBlock.FontSize = GetFontSize(txt.Text, new Size(txt.ActualWidth, txt.ActualHeight), new Typeface(txt.FontFamily, txt.FontStyle, txt.FontWeight, txt.FontStretch));
    

    Edit:

    For the purposes of practicality, you might want to be able to have the text to center vertically in this predefined bounding rectangle. A good way to do that is to wrap your TextBlock inside another object such as a Border element. This way you can tell your TextBlock to align in the center of the border and it can auto-size to fit its content.