高级文本格式设置

Windows Presentation Foundation (WPF) 提供了一组稳定可靠的 APIs,用于在应用程序中加入文本。 布局和user interface (UI) APIs(如 TextBlock)为文本呈现提供了最常见的通用元素。 绘图 APIs(如 GlyphRunDrawingFormattedText)提供一种方法,可在绘图中加入已设置了格式的文本。 在最高级别,WPF 提供了一个可扩展的文本格式引擎,用于控制文本呈现的各个方面,如文本存储管理、文本运行格式管理和嵌入对象管理。

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

注意注意

本文档中的所有代码示例都可以在 Advanced Text Formatting Sample(高级文本格式设置示例)中找到。

本主题包括下列各节。

  • 系统必备组件
  • 高级文本格式
  • 使用文本格式设置程序
  • 实现客户端文本存储
  • 提供文本运行
  • 指定格式设置属性
  • 相关主题

系统必备组件

本主题假定您熟悉较高级别的用于呈现文本的 APIs。 大部分用户方案都不需要本主题中所讨论的高级文本格式 APIs。 有关不同文本 APIs 的介绍,请参见 WPF 中的文档

高级文本格式

WPF 中的文本布局和 UI 控件提供了格式设置属性,通过这些属性,您可以轻松地在应用程序中加入已设置了格式的文本。 这些控件将公开许多用于处理文本呈现(包括字样、大小和颜色)的属性。 在一般情况下,这些控件可以处理应用程序中的大部分文本呈现。 但是,某些高级方案要求控制文本存储和文本呈现。 为此,WPF 提供了一个可扩展的文本格式引擎。

在 WPF 中找到的高级文本格式功能包括文本格式引擎、文本存储、文本运行和格式设置属性。 文本格式引擎 TextFormatter 可创建用于呈现的文本行。 这是通过初始化行格式设置过程并调用文本格式设置程序的 FormatLine 而实现的。 文本格式设置程序通过调用文本存储的 GetTextRun 方法而从该存储中检索文本运行。 然后,文本格式设置程序将 TextRun 对象转换成 TextLine 对象,并提供给应用程序进行检查或显示。

使用文本格式设置程序

TextFormatter 是一种 WPF 文本格式引擎,用于提供文本行格式设置和换行服务。 文本格式设置程序可以处理各种文本字符格式和段落样式,并提供对国际文本布局的支持。

与传统文本 API 不同的是,TextFormatter 通过一组回调方法来与文本布局客户端交互。 它要求客户端在 TextSource 类的实现中提供这些方法。 下面的关系图说明了客户端应用程序和 TextFormatter 之间的文本布局交互。

应用程序和 TextFormatter 之间的交互

文本布局客户端和 TextFormatter 示意图

文本格式设置程序用于从文本存储中检索已设置了格式的文本行(即 TextSource 的实现)。 这是通过首先使用 Create 方法创建文本格式设置程序实例来完成的。 此方法将创建一个文本格式设置程序实例,并设置最大行高值和行宽值。 一旦创建文本格式设置程序实例,即可通过调用 FormatLine 方法来启动行创建过程。 TextFormatter 将回调文本源以便检索构成行的文本运行的文本和格式设置参数。

下面的示例演示文本存储的格式设置过程。 TextFormatter 对象用于从文本存储中检索文本行,然后设置该文本行的格式以便在 DrawingContext 中绘制。

         ' 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
// 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;

实现客户端文本存储

扩展文本格式引擎时,需要实现和管理文本存储的各个方面。 该任务不是无关紧要的。 文本存储负责跟踪文本运行属性、段落属性、嵌入对象和其他类似内容。 另外,它还为文本格式设置程序提供了单独的 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 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
// 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);
}
注意注意

在此示例中,文本存储为所有文本提供了相同的文本属性。高级文本存储需要实现各自的范围管理,以便单个字符具有不同的属性。

指定格式设置属性

TextRun 对象是使用文本存储提供的属性设置格式的。 这些属性包括分为两种类型:TextParagraphPropertiesTextRunPropertiesTextParagraphProperties 可处理涉及段落的属性,如 TextAlignmentFlowDirectionTextRunProperties 是一系列属性(如前景画笔、Typeface 和字号),这些属性对段落中的每个文本运行可能会有所不同。 若要实现自定义段落和自定义文本运行属性类型,应用程序必须创建分别派生自 TextParagraphPropertiesTextRunProperties 的类。

请参见

概念

WPF 中的版式

WPF 中的文档