Яндекс.Метрика

    Песочница

    Два способа задать межсимвольный интервал в WPF

    Введение в суть задачи


    В одном из WPF — проектов возникла необходимость задать ширину пробела (расстояние) между символами для текста в элементе TextBlock. Точнее было нужно заполнить с помощью принтера бланк примерно такого типа (рис. 1). То есть важно подобрать интервал между символами, соответствующий интервалу между клетками формы. И я не сомневался, что в WPF для TextBlock существует нужное свойство, однако гугление показало, что это не так.

    image
    Рис. 1. Пример формы, которую необходимо заполнить.

    Первый способ решения


    Для начала в голову пришло использовать свойство FontStretch (Получает или задает характеристики растягивания шрифтов верхнего уровня для объекта TextBlock). Но возникла небольшая проблема: элемент никак не реагировал на различные значения свойства FontStretch. Оказалось, что не все шрифты поддерживают данное свойство. Но выход всё-таки нашелся:

      <Application.Resources>
        <ScaleTransform x:Key="FontStretchCondensed" ScaleX="0.8" />
        <ScaleTransform x:Key="FontStretchExpanded" ScaleX="1.2" />
         <Style TargetType="TextBlock">
          <Stylе.Setters>
             <Setter Property="LayoutTransform" Value="{StaticResource FontStretchCondensed}" />
          </Style.Setters>
        </Stylе>
      </Application.Resources>


    * This source code was highlighted with Source Code Highlighter.


    Удалось во-первых задать точное значение растяжения, а не дискретную величину от 1 до 9 (как у свойства FontStretch), во-вторых растянуть любой шрифт.
    Однако, данный подход оказался неприемлем, так как растягиваются сами символы, а расстояние между ними остается неизменным. То есть, при малейшей ошибке подачи бумаги в принтер, символ вылез бы за пределы отведенной ему клетки.

    Второй и более корректный способ решения задачи


    Поиск метода задать ширину пробела был продолжен, однако не дал результатов. Оказалось, что нужного свойства ни у одного контрола в WPF просто нет. Выход виделся только один: написать контрол с поддержкой растягивания шрифтов. Код контрола:

      class MyTextBlock : Control
      {
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",
                                      typeof(string),
                                      typeof(MyTextBlock));
        public string Text
        {
          get { return GetValue(TextProperty) as string; }
          set { SetValue(TextProperty, value); }
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
          base.OnRender(drawingContext);

          if (Text != null)
          {

            var widthPerChar = ActualWidth / Text.Length;
            var currentPosition = 0d;

            foreach (var ch in Text)
            {
              drawingContext.DrawText(new FormattedText(ch.ToString().ToUpper(), CultureInfo.CurrentUICulture, FlowDirection.LeftToRight,
                new Typeface(FontFamily.Source), FontSize, Foreground), new Point(currentPosition, 0));
              currentPosition += widthPerChar;
            }
          }
        }

      }


    * This source code was highlighted with Source Code Highlighter.


    Конечно, это не самое изящное решение, зато точно соответствует поставленной задаче. Теперь можно задать контролу MyTextBlock свойство Width, соответствующее длине поля на рис. 1 и текст будет растянут на нужную ширину с помощью увеличения расстояния между символами.