Умный TextBlock
Недавно появилась необходимость отображения текста в определенной ограниченной области с переменным размером шрифта: таким образом, чтобы текст полностью заполнял отведенную ему область.
Такой способ полностью подходит для небольших строковых переменных, но, к сожалению, в этом случае не работает стандартная функциональность переноса строк — TextWrapping, и строка любой длины будет помещена в одну линию текста.
Таким образом, решением проблемы стало реализация класса TextBlockFilled, который наследуется от Control и на этапе отображения итеративно подбирает размер шрифта.
Шаг для итерации лучше подбирать в зависимости от высоты отведенного пространства. Для меня шаг в ActualHeight / 20 оказался наиболее подходящим.
Для более широкой области применения класса имеет смысл сделать доступными из вне некоторые свойства оформления (такие как шрифт, цвет…).
Решение №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 комментариев