Share via


高级文本格式设置

Windows Presentation Foundation (WPF) 提供了一组可靠的 API,用于在应用程序中包含文本。 布局和用户界面 (UI) API,例如 TextBlock,为文本展示提供最常用和常规用途的元素。 绘图 API,例如 GlyphRunDrawingFormattedText,为在绘图中包含格式化文本提供方法。 在最高级别,WPF 提供了一个可扩展的文本格式引擎,用于控制文本展示的各个方面,如文本存储管理、文本运行格式管理和嵌入对象管理。

本主题介绍了 WPF 文本格式化。 它重点介绍了客户端实现和 WPF 文本格式化引擎的使用。

注意

本文档中的所有代码示例都可以在高级文本格式化示例中找到。

先决条件

本主题假定你熟悉用于文本展示的较高级别 API。 大部分用户方案都不需要在本主题中所讨论的高级文本格式化 API。 有关不同文本 API 的介绍,请参阅 WPF 中的文档

高级文本格式设置

WPF 中的文本布局和 UI 控件提供了格式化属性,通过这些属性,可轻松地在应用程序中加入格式化文本。 这些控件会公开许多用于处理文本呈现(包括字样、大小和颜色)的属性。 一般情况下,这些控件可以处理应用程序中的大部分文本呈现。 但是,某些高级方案要求控制文本存储和文本呈现。 为此,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 对象。

若要处理文本存储的虚拟化,该文本存储必须派生自 TextSourceTextSource 定义了文本格式化程序用于从文本存储中检索文本运行的方法。 GetTextRun 文本格式化程序用于检索在行格式化中使用的文本运行的方法。 对 GetTextRun 的调用由文本格式化程序反复生成,直到出现下列条件之一:

  • 返回一个 TextEndOfLine 或一个子类。

  • 文本运行的累积宽度超出在创建文本格式化程序的调用或对文本格式化程序的 FormatLine 方法的调用中指定的最大行宽。

  • 返回 Unicode 换行序列,如“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 对象通过使用文本存储提供的属性进行格式化。 这些属性分为两种类型,TextParagraphPropertiesTextRunPropertiesTextParagraphProperties 处理段落包含属性,例如 TextAlignmentFlowDirectionTextRunProperties 是每个在段落内运行的文本可能不同的属性,例如前景画笔 Typeface,和字号。 要实现自定义段落和自定义文本运行属性类型,应用程序必须创建分别从 TextParagraphPropertiesTextRunProperties 派生出来的类。

另请参阅