Дополнительное форматирование текста

Windows Presentation Foundation (WPF) предоставляет широкий набор API для включения текста в приложения. API макета и пользовательского интерфейса, такие как TextBlock, обеспечивают наиболее распространенные элементы для представления текста. API рисования, такие как GlyphRunDrawing и FormattedText, предоставляют средства для включения форматированного текста в рисунки. На наиболее продвинутом уровне WPF обеспечивает расширяемый механизм форматирования текста для управления каждым аспектом представления текста: управление хранением текста, форматированием фрагментов текста и внедренными объектами.

В этом разделе содержится общая информация о форматировании текста в WPF. Раздел посвящен реализации клиента и использованию механизма форматирования текста WPF.

Примечание.

Все примеры кода в этом документе можно найти в разделе Пример расширенного форматирования текста.

Необходимые компоненты

Предполагается, что вы знакомы с более общими API, используемыми для представления текста. Большинство сценариев не требует API расширенного форматирования текста, которые рассматриваются в данном разделе. Общие сведения о других API текста см. в разделе Документы в WPF.

Дополнительное форматирование текста

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

Функции расширенного форматирования текста в WPF состоят из механизма форматирования, хранилища текста, фрагментов текста и свойств форматирования. Механизм форматирования текста, TextFormatter, создает строки текста, используемые для представления. Это достигается путем запуска процесса форматирования строк и вызовом метода FormatLine модуля форматирования текста. Модуль форматирования текста получает фрагменты текста из хранилища текста, вызывая метод GetTextRun хранилища. Затем объекты TextRun формируются в объекты TextLine с помощью модуля форматирования текста и передаются в приложение для проверки или отображения.

Использование модуля форматирования текста

TextFormatter — это модуль форматирования текста WPF, который предоставляет службы для форматирования и переноса строк текста. Модуль форматирования текста может обрабатывать различные форматы текстовых символов и стили абзацев и включает поддержку международного макета текста.

В отличие от традиционных текстовых API-интерфейсов TextFormatter взаимодействует с клиентом макета текста посредством набора методов обратного вызова. Для этого требуется, чтобы клиент предоставил данные методы в реализации класса TextSource. Следующая диаграмма иллюстрирует взаимодействие макета текста между клиентским приложением и объектом TextFormatter.

Diagram of text layout client and TextFormatter

Модуль форматирования текста используется для извлечения форматированных строк текста из хранилища текста, которое является реализацией TextSource. Для этого сначала создается экземпляр модуля форматирования текста с помощью метода Create. Этот метод создает экземпляр модуля форматирования текста и задает максимальные значения высоты и ширины строки. Как только экземпляр модуля форматирования текста создан, запускается процесс создания строки путем вызова метода FormatLine. TextFormatter осуществляет обратный вызов к источнику текста для получения текста и параметров форматирования для фрагментов текста, формирующих строку.

В следующем примере показан процесс форматирования хранилища текста. Объект TextFormatter используется для извлечения строк текста из хранилища текста и последующего форматирования строк текста для рисования в контексте DrawingContext.

// Create a DrawingGroup object for storing formatted text.
textDest = new DrawingGroup();
DrawingContext dc = textDest.Open();

// Update the text store.
_textStore.Text = textToFormat.Text;
_textStore.FontRendering = _currentRendering;

// Create a TextFormatter object.
TextFormatter formatter = TextFormatter.Create();

// Format each line of text from the text store and draw it.
while (textStorePosition < _textStore.Text.Length)
{
   // Create a textline from the text store using the TextFormatter object.
   using (TextLine myTextLine = formatter.FormatLine(
       _textStore,
       textStorePosition,
       96*6,
       new GenericTextParagraphProperties(_currentRendering),
       null))
   {
       // Draw the formatted text into the drawing context.
       myTextLine.Draw(dc, linePosition, InvertAxes.None);

       // Update the index position in the text store.
       textStorePosition += myTextLine.Length;

       // Update the line position coordinate for the displayed line.
       linePosition.Y += myTextLine.Height;
   }
}

// Persist the drawn text content.
dc.Close();

// Display the formatted text in the DrawingGroup object.
myDrawingBrush.Drawing = textDest;
' Create a DrawingGroup object for storing formatted text.
textDest = New DrawingGroup()
Dim dc As DrawingContext = textDest.Open()

' Update the text store.
_textStore.Text = textToFormat.Text
_textStore.FontRendering = _currentRendering

' Create a TextFormatter object.
Dim formatter As TextFormatter = TextFormatter.Create()

' Format each line of text from the text store and draw it.
Do While textStorePosition < _textStore.Text.Length
   ' Create a textline from the text store using the TextFormatter object.
   Using myTextLine As TextLine = formatter.FormatLine(_textStore, textStorePosition, 96*6, New GenericTextParagraphProperties(_currentRendering), Nothing)
       ' Draw the formatted text into the drawing context.
       myTextLine.Draw(dc, linePosition, InvertAxes.None)

       ' Update the index position in the text store.
       textStorePosition += myTextLine.Length

       ' Update the line position coordinate for the displayed line.
       linePosition.Y += myTextLine.Height
   End Using
Loop

' Persist the drawn text content.
dc.Close()

' Display the formatted text in the DrawingGroup object.
myDrawingBrush.Drawing = textDest

Реализация клиентского хранилища текста

При расширении механизма форматирования текста необходимо реализовать все аспекты хранилища текста и управлять ими. Это довольно сложная задача. Хранилище текста отвечает за отслеживание свойств фрагментов текста, свойств абзаца, встроенных объектов и других подобных элементов. Оно также предоставляет модуль форматирования текста с отдельными объектами TextRun, которые этот модуль использует для создания объектов TextLine.

Для обработки виртуализации хранилища текста это хранилище должно быть производным от TextSource. TextSource определяет метод, используемый модулем форматирования текста для извлечения фрагментов текста из хранилища текста. GetTextRun — это метод, используемый модулем форматирования текста для извлечения фрагментов текста, используемых в форматировании строки. Вызов GetTextRun неоднократно осуществляется модулем форматирования текста, пока не произойдет одно из следующих условий:

  • Возвращается TextEndOfLine или подкласс.

  • суммарная ширина фрагментов текста превышает ширину строк, указанную в вызове для создания модуля форматирования текста или в вызове метода FormatLine модуля форматирования текста;

  • Возвращается последовательность новой строки Юникода, такая как CF, LF или CRLF.

Предоставление фрагментов текста

Ядром процесса форматирования текста является взаимодействие между модулем форматирования текста и хранилищем текста. Реализация TextSource предоставляет модулю форматирования текста объекты TextRun и свойства, с помощью которых форматируются фрагменты текста. Это взаимодействие обрабатывается методом GetTextRun, который вызывается модулем форматирования текста.

В следующей таблице представлены некоторые предопределенные объекты TextRun.

Тип TextRun Использование
TextCharacters Специализированный фрагмент текста, который используется для передачи представления глифов символов обратно в модуль форматирования текста.
TextEmbeddedObject Специализированный фрагмент текста, который используется для предоставления содержимого, в котором измерение, проверка нажатия и рисование выполняются в целом (например, кнопка или изображение в тексте).
TextEndOfLine Специализированный фрагмент текста, который используется для обозначения конца строки.
TextEndOfParagraph Специализированный фрагмент текста, который используется для обозначения конца абзаца.
TextEndOfSegment Специализированный фрагмент текста, который используется для обозначения конца сегмента, например конца области, затронутой предыдущим фрагментом TextModifier.
TextHidden Специализированный фрагмент текста, который используется для отметки диапазона скрытых символов.
TextModifier Специализированный фрагмент текста, который используется для изменения свойств фрагментов текста в своей области. Область расширяется до следующего соответствующего фрагмента текста TextEndOfSegment или следующего абзаца TextEndOfParagraph.

Для всех предопределенных объектов TextRun допускается создание подклассов. Это позволяет источнику текста предоставлять модуль форматирования текста с фрагментами текста, включающими пользовательские данные.

В следующем примере демонстрируется метод GetTextRun. Это хранилище текста возвращает объекты TextRun для обработки модулем форматирования текста.

// Used by the TextFormatter object to retrieve a run of text from the text source.
public override TextRun GetTextRun(int textSourceCharacterIndex)
{
   // Make sure text source index is in bounds.
   if (textSourceCharacterIndex < 0)
      throw new ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.");
   if (textSourceCharacterIndex >= _text.Length)
   {
      return new TextEndOfParagraph(1);
   }

   // Create TextCharacters using the current font rendering properties.
   if (textSourceCharacterIndex < _text.Length)
   {
      return new TextCharacters(
         _text,
         textSourceCharacterIndex,
         _text.Length - textSourceCharacterIndex,
         new GenericTextRunProperties(_currentRendering));
   }

   // Return an end-of-paragraph if no more text source.
   return new TextEndOfParagraph(1);
}
' Used by the TextFormatter object to retrieve a run of text from the text source.
Public Overrides Function GetTextRun(ByVal textSourceCharacterIndex As Integer) As TextRun
   ' Make sure text source index is in bounds.
   If textSourceCharacterIndex < 0 Then
      Throw New ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.")
   End If
   If textSourceCharacterIndex >= _text.Length Then
      Return New TextEndOfParagraph(1)
   End If

   ' Create TextCharacters using the current font rendering properties.
   If textSourceCharacterIndex < _text.Length Then
      Return New TextCharacters(_text, textSourceCharacterIndex, _text.Length - textSourceCharacterIndex, New GenericTextRunProperties(_currentRendering))
   End If

   ' Return an end-of-paragraph if no more text source.
   Return New TextEndOfParagraph(1)
End Function

Примечание.

В этом примере хранилище текста предоставляет одни и те же свойства текста всему тексту. Расширенные хранилища текста могут потребоваться для реализации собственного управления диапазонами, чтобы позволить отдельным символам иметь разные свойства.

Задание свойств форматирования

Форматирование объектов TextRun выполняется с помощью свойств, предоставляемых хранилищем текста. Эти свойства домена бывают двух типов: TextParagraphProperties и TextRunProperties. TextParagraphProperties обрабатывают свойства абзаца, такие как TextAlignment и FlowDirection. Свойства TextRunProperties — это свойства, которые могут быть разными для каждого фрагмента текста в абзаце, например кисть переднего плана, Typeface и размер шрифта. Для реализации типов свойств пользовательских абзацев и пользовательских фрагментов текста приложение должно создавать классы, производные от TextParagraphProperties и TextRunProperties соответственно.

См. также