Dynamic Fontsize for TextBlock with packaging

I have a fixed size TextBlock that wraps text. sometimes short sometimes long.

If the text reaches the end, it does not appear completely as

enter image description here

How can I make Fontsize flexible so that the text matches a TextBox with a static size?

+4
source share
2 answers

My solution is as follows:

Set the fontsize value to a value that you no longer need. The ActualHeight value for the TextBlock changes when the font size changes or when the content changes. I built a solution based on this. You must create an event handler for the SizeChanged event and write the following code into it.

private void MyTextBlock_SizeChanged(object sender, SizeChangedEventArgs e) { double desiredHeight = 80; // Here you'll write the height you want the text to use if (this.MyTextBlock.ActualHeight > desiredHeight) { // You want to know, how many times bigger the actual height is, than what you want to have. // The reason for Math.Sqrt() is explained below in the text. double fontsizeMultiplier = Math.Sqrt(desiredHeight / this.MyTextBlock.ActualHeight); // Math.Floor() can be omitted in the next line if you don't want a very tall and narrow TextBox. this.MyTextBlock.FontSize = Math.Floor(this.MyTextBlock.FontSize * fontsizeMultiplier); } this.MyTextBlock.Height = desiredHeight; // ActualHeight will be changed if the text is too big, after the text was resized, but in the end you want the box to be as big as the desiredHeight. } 

The reason I used Math.Sqrt () is because if you set the font size two times larger than before, then the area in which the font will be used will be a quarter smaller, and then (since this became half wide and half high as before). And you clearly want to keep the width of the TextBox and only change its height.

If you're lucky, the font size will be appropriate after this method is executed once. However, depending on the text that will be repackaged after changing the font size, you may be so lucky that the text will be one line longer than you would like. Fortunately, the event handler will be called again (because you changed the font size), and the resizing will be performed again if it is still too large.

I tried, it was fast, and the results looked good. However, I can imagine that in a really bad choice of text and height, the correct font size will be achieved after several iterations. This is why I used Math.Floor (). All in all, it doesn't really matter if the font size is at the end of 12.34 or 12, and so I wonโ€™t worry about โ€œunluckyโ€ text that will be too long to render. But I think that Math.Floor () can be omitted if you do not want to have a very high text box (e.g. 2000 pixels) with lots of text.

+10
source

Here's the complete solution, including the ability to set maxheight / maxwidth, and it is calculated directly on the render:

 public class TextBlockAutoShrink : TextBlock { private double _defaultMargin = 6; private Typeface _typeface; static TextBlockAutoShrink() { TextBlock.TextProperty.OverrideMetadata(typeof(TextBlockAutoShrink), new FrameworkPropertyMetadata(new PropertyChangedCallback(TextPropertyChanged))); } public TextBlockAutoShrink() : base() { _typeface = new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch, this.FontFamily); base.DataContextChanged += new DependencyPropertyChangedEventHandler(TextBlockAutoShrink_DataContextChanged); } private static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { var t = sender as TextBlockAutoShrink; if (t != null) { t.FitSize(); } } void TextBlockAutoShrink_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { FitSize(); } protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { FitSize(); base.OnRenderSizeChanged(sizeInfo); } private void FitSize() { FrameworkElement parent = this.Parent as FrameworkElement; if (parent != null) { var targetWidthSize = this.FontSize; var targetHeightSize = this.FontSize; var maxWidth = double.IsInfinity(this.MaxWidth) ? parent.ActualWidth : this.MaxWidth; var maxHeight = double.IsInfinity(this.MaxHeight) ? parent.ActualHeight : this.MaxHeight; if (this.ActualWidth > maxWidth) { targetWidthSize = (double)(this.FontSize * (maxWidth / (this.ActualWidth + _defaultMargin))); } if (this.ActualHeight > maxHeight) { var ratio = maxHeight / (this.ActualHeight); // Normalize due to Height miscalculation. We do it step by step repeatedly until the requested height is reached. Once the fontsize is changed, this event is re-raised // And the ActualHeight is lowered a bit more until it doesnt enter the enclosing If block. ratio = (1 - ratio > 0.04) ? Math.Sqrt(ratio) : ratio; targetHeightSize = (double)(this.FontSize * ratio); } this.FontSize = Math.Min(targetWidthSize, targetHeightSize); } } } 
0
source

All Articles