Умный TextBlock

Недавно появилась необходимость отображения текста в определенной ограниченной области с переменным размером шрифта: таким образом, чтобы текст полностью заполнял отведенную ему область.
image

Решение №1

Первое, бросившееся в глаза, решение этой проблемы было использование Viewbox, внутри которого расположен обычный TextBlock:
<Viewbox Stretch="Uniform">
 <TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Viewbox>

Такой способ полностью подходит для небольших строковых переменных, но, к сожалению, в этом случае не работает стандартная функциональность переноса строк — TextWrapping, и строка любой длины будет помещена в одну линию текста.

Решение №2

Так как для меня была важна возможность автоматического размещения текста в несколько строк, я продолжил поиски и остановился на классе FormattedText. У экземпляра этого класса имеется 2 интересующих меня свойства: MaxTextWidth, MaxTextHeight. Присваивая значения этим свойствам, мы ограничиваем область последующей отрисовки текста.

Таким образом, решением проблемы стало реализация класса TextBlockFilled, который наследуется от Control и на этапе отображения итеративно подбирает размер шрифта.
public class TextBlockFilled : Control
 {
 public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",
 typeof(string),
 typeof(TextBlockFilled));

public string Text
 {
 get { return GetValue(TextProperty) as string; }
 set { SetValue(TextProperty, value); }
 }

protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
 {
 base.OnRender(drawingContext);
 FormattedText formattedText = formattedText = new FormattedText(
 Text,
 CultureInfo.GetCultureInfo("en-us"),
 FlowDirection.LeftToRight,
 new Typeface("Verdana"),
 ActualHeight,
 Brushes.Black);
 formattedText.TextAlignment = TextAlignment.Center;
 formattedText.MaxTextWidth = ActualWidth - Padding.Left - Padding.Right;
 formattedText.Trimming = TextTrimming.None;
 double step = ActualHeight / 20;
 for (double i = ActualHeight - step; i >= step; i -= step)
 {
 if (formattedText.Height <= ActualHeight - Padding.Top - Padding.Bottom) break;
 formattedText.SetFontSize(i);
 }
 formattedText.MaxTextHeight = ActualHeight;
 drawingContext.DrawText(formattedText, new Point(Padding.Left, Padding.Top));
 }
 }

Шаг для итерации лучше подбирать в зависимости от высоты отведенного пространства. Для меня шаг в ActualHeight / 20 оказался наиболее подходящим.

Для более широкой области применения класса имеет смысл сделать доступными из вне некоторые свойства оформления (такие как шрифт, цвет…).


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.