优化性能:文本

WPF 支持通过使用功能丰富的用户界面 (UI) 控件来呈现文本内容。 通常可以将文本呈现分为三层:

  1. 直接使用 GlyphsGlyphRun 对象。

  2. 使用 FormattedText 对象。

  3. 使用高级控件,如 TextBlockFlowDocument 对象。

本主题提供文本呈现性能方面的建议。

字形级别的呈现文本

Windows Presentation Foundation (WPF) 为希望在格式化后截取和保留文本的客户提供高级文本支持,包括可直接访问 Glyphs 的字形级标记。 这些功能为以下每种方案中不同的文本呈现要求提供关键支持。

  • 固定格式文档的屏幕显示。

  • 打印方案。

    • Extensible Application Markup Language (XAML) 作为设备打印机语言。

    • Microsoft XPS 文档编写器。

    • 以前的打印机驱动程序,从 Win32 应用程序输出为固定格式。

    • 打印后台处理格式。

  • 固定格式的文档演示,包括以前版本的 Windows 客户端和其他计算设备。

注意

GlyphsGlyphRun 专为固定格式的文档演示和打印方案而设计。 WPF 为常规布局和用户界面 (UI) 方案提供多个元素,例如 LabelTextBlock。 有关布局和 UI 方案的详细信息,请参阅 WPF 中的版式

以下示例演示如何定义 XAML 中 Glyphs 对象的属性。 示例假定本地计算机上的 C:\WINDOWS\Fonts 文件夹中安装了 Arial、Courier New 和 Times New Roman 字体。

<!-- The example shows how to use a Glyphs object. -->
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >

   <StackPanel Background="PowderBlue">

      <Glyphs
         FontUri             = "C:\WINDOWS\Fonts\TIMES.TTF"
         FontRenderingEmSize = "100"
         StyleSimulations    = "BoldSimulation"
         UnicodeString       = "Hello World!"
         Fill                = "Black"
         OriginX             = "100"
         OriginY             = "200"
      />

   </StackPanel>
</Page>

使用 DrawGlyphRun

如果具有自定义控件,并且要呈现字形,请使用 DrawGlyphRun 方法。

通过使用 FormattedText 对象,WPF 还为自定义文本格式设置提供较低级别服务。 在 Windows Presentation Foundation (WPF) 中呈现文本最高效的方法是使用 GlyphsGlyphRun 生成字形级别的文本内容。 但是,高效的代价是丢失了丰富的文本格式设置的使用便捷性,这些文本格式设置是 Windows Presentation Foundation (WPF) 控件的内置功能,如 TextBlockFlowDocument

FormattedText 对象

使用 FormattedText 对象可以绘制多行文本,可对文本中的每个字符单独设置格式。 有关详细信息,请参阅绘制格式化文本

若要创建格式化文本,请调用 FormattedText 构造函数来创建 FormattedText 对象。 创建初始格式化文本字符串后,便可应用某一范围的格式样式。 如果应用程序要实现其自身的布局,则相对于使用 TextBlock 等控件而言,FormattedText 对象是更好的选择。 有关 FormattedText 对象的详细信息,请参阅绘制格式化文本

FormattedText 对象提供低级别的文本格式设置功能。 可向一个或多个字符应用多种格式样式。 例如,可以同时调用 SetFontSizeSetForegroundBrush 方法来更改文本中前五个字符的格式。

以下代码示例创建 FormattedText 对象并对其进行呈现。

protected override void OnRender(DrawingContext drawingContext)
{
    string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor";

    // Create the initial formatted text string.
    FormattedText formattedText = new FormattedText(
        testString,
        CultureInfo.GetCultureInfo("en-us"),
        FlowDirection.LeftToRight,
        new Typeface("Verdana"),
        32,
        Brushes.Black);

    // Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
    formattedText.MaxTextWidth = 300;
    formattedText.MaxTextHeight = 240;

    // Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
    // The font size is calculated in terms of points -- not as device-independent pixels.
    formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5);

    // Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
    formattedText.SetFontWeight(FontWeights.Bold, 6, 11);

    // Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
    formattedText.SetForegroundBrush(
                            new LinearGradientBrush(
                            Colors.Orange,
                            Colors.Teal,
                            90.0),
                            6, 11);

    // Use an Italic font style beginning at the 28th character and continuing for 28 characters.
    formattedText.SetFontStyle(FontStyles.Italic, 28, 28);

    // Draw the formatted text string to the DrawingContext of the control.
    drawingContext.DrawText(formattedText, new Point(10, 0));
}
Protected Overrides Sub OnRender(ByVal drawingContext As DrawingContext)
    Dim testString As String = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"

    ' Create the initial formatted text string.
    Dim formattedText As New FormattedText(testString, CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, New Typeface("Verdana"), 32, Brushes.Black)

    ' Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
    formattedText.MaxTextWidth = 300
    formattedText.MaxTextHeight = 240

    ' Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
    ' The font size is calculated in terms of points -- not as device-independent pixels.
    formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5)

    ' Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
    formattedText.SetFontWeight(FontWeights.Bold, 6, 11)

    ' Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
    formattedText.SetForegroundBrush(New LinearGradientBrush(Colors.Orange, Colors.Teal, 90.0), 6, 11)

    ' Use an Italic font style beginning at the 28th character and continuing for 28 characters.
    formattedText.SetFontStyle(FontStyles.Italic, 28, 28)

    ' Draw the formatted text string to the DrawingContext of the control.
    drawingContext.DrawText(formattedText, New Point(10, 0))
End Sub

FlowDocument、TextBlock 和 Label 控件

WPF 包括多个用于在屏幕中绘制文本的控件。 每个控件都面向不同的方案,并具有自己的功能和限制列表。

FlowDocument 对性能的影响比 TextBlock 或 Label 大

通常,当需要支持有限的文本时应使用 TextBlock 元素,例如需要句子简短的用户界面 (UI)。 当需要最少的文本支持时,可以使用 LabelFlowDocument 元素是可重流动文档的容器,该文档支持丰富的内容呈现,因此,相对于使用 TextBlockLabel 控件,该元素具有更好的性能效果。

有关 FlowDocument 的详细信息,请参阅流文档概述

避免在 FlowDocument 中使用 TextBlock

TextBlock 元素派生自 UIElementRun 元素派生自 TextElement,后者的使用成本比 UIElement 派生的对象低。 如果可能,请使用 Run 而不是 TextBlockFlowDocument 中显示文本内容。

以下标记示例演示了在 FlowDocument 中设置文本内容的两种方法:

<FlowDocument>

  <!-- Text content within a Run (more efficient). -->
  <Paragraph>
    <Run>Line one</Run>
  </Paragraph>

  <!-- Text content within a TextBlock (less efficient). -->
  <Paragraph>
    <TextBlock>Line two</TextBlock>
  </Paragraph>

</FlowDocument>

避免使用 Run 来设置文本属性

一般情况下,相对于完全不使用显式 Run 对象,在 TextBlock 中使用 Run 占用的资源更多。 如果使用 Run 来设置文本属性,请改为直接在 TextBlock 上设置这些属性。

以下标记示例演示了设置文本属性的两种方法,在本例中为设置 FontWeight 属性:

<!-- Run is used to set text properties. -->
<TextBlock>
  <Run FontWeight="Bold">Hello, world</Run>
</TextBlock>

<!-- TextBlock is used to set text properties, which is more efficient. -->
<TextBlock FontWeight="Bold">
  Hello, world
</TextBlock>

下表演示显示 1000 个 TextBlock 对象(使用和不使用显式 Run)的成本。

TextBlock 类型 创建时间 (ms) 呈现时间 (ms)
运行设置文本属性 146 540
TextBlock 设置文本属性 43 453

避免对 Label.Content 属性进行数据绑定

想象你有一个经常从 String 源更新的 Label 对象。 将 Label 元素的 Content 属性数据绑定到 String 源对象时,可能会遇到性能不佳的情况。 每次更新源 String 时,将丢弃旧的 String 对象,并重新创建新的 String,因为 String 对象是不可变的,不能对其进行修改。 而这又会导致 Label 对象的 ContentPresenter 丢弃其旧的内容,并重新生成新的内容,以显示新的 String

此问题的解决方法很简单。 如果 Label 未设置为自定义 ContentTemplate 值,请将 Label 替换为 TextBlock 并将其 Text 属性数据绑定到源字符串。

数据绑定属性 更新时间 (ms)
Label.Content 835
TextBlock.Text 242

Hyperlink 对象为内联级流内容元素,允许承载流内容中的超链接。

通过在同一 TextBlock 中将多个 Hyperlink 元素组合在一起,可以优化它们的使用。 这有助于最小化在应用程序中创建的对象的数量。 例如,可显示多个超链接,如下所示:

MSN 主页 | 我的 MSN

以下标记示例演示多个用来显示超链接的 TextBlock 元素:

<!-- Hyperlinks in separate TextBlocks. -->
<TextBlock>
  <Hyperlink TextDecorations="None" NavigateUri="http://www.msn.com">MSN Home</Hyperlink>
</TextBlock>

<TextBlock Text=" | "/>

<TextBlock>
  <Hyperlink TextDecorations="None" NavigateUri="http://my.msn.com">My MSN</Hyperlink>
</TextBlock>

以下标记示例演示了一种更有效的超链接显示方式,这次使用单个 TextBlock

<!-- Hyperlinks combined in the same TextBlock. -->
<TextBlock>
  <Hyperlink TextDecorations="None" NavigateUri="http://www.msn.com">MSN Home</Hyperlink>
  
  <Run Text=" | " />
  
  <Hyperlink TextDecorations="None" NavigateUri="http://my.msn.com">My MSN</Hyperlink>
</TextBlock>

TextDecoration 对象是可以添加到文本中的视觉装饰;但是会占用实例化的资源。 如果大范围使用 Hyperlink 元素,请考虑仅在触发事件时显示下划线,例如 MouseEnter 事件。 有关详细信息,请参阅指定是否为超链接添加下划线

下图显示了 MouseEnter 事件如何触发带下划线的超链接:

Hyperlinks displaying TextDecorations

以下标记示例演示使用和不使用下划线定义的 Hyperlink

<!-- Hyperlink with default underline. -->
<Hyperlink NavigateUri="http://www.msn.com">
  MSN Home
</Hyperlink>

<Run Text=" | " />

<!-- Hyperlink with no underline. -->
<Hyperlink Name="myHyperlink" TextDecorations="None"
           MouseEnter="OnMouseEnter"
           MouseLeave="OnMouseLeave"
           NavigateUri="http://www.msn.com">
  My MSN
</Hyperlink>

下表演示显示 1000 个 Hyperlink 元素(使用和不使用下划线)的性能成本。

超链接 创建时间 (ms) 呈现时间 (ms)
使用下划线 289 1130
不使用下划线 299 776

文本格式设置功能

WPF 提供丰富的文本格式设置服务,如自动断字。 这些服务可能会影响应用程序性能,应仅在需要时使用。

避免不必要地使用断字

自动断字功能查找文本行的连字符断点,并允许在 TextBlockFlowDocument 对象的行中添加中断位置。 默认禁用这些对象中的自动断字功能。 通过将该对象的 IsHyphenationEnabled 属性设置为 true,可以启用此功能。 但是,启用此功能会导致 WPF 启动组件对象模型 (COM) 互操作性,这可能会影响应用程序的性能。 除非需要,否则不建议使用自动断字功能。

谨慎使用图形

Figure 元素代表在某页内容中绝对可定位的一部分流内容。 在某些情况下,如果 Figure 的位置与已布局的内容发生冲突,则可能会导致整个页面自动重新格式化。可以通过将 Figure 元素彼此相邻分组或在固定页面大小方案中将其声明在内容顶部附近,来最大限度地减少不必要的重新格式化的可能性。

最佳段落

FlowDocument 对象的最佳段落功能对段落进行布局,以便尽可能均匀地分布空白区域。 默认禁用最佳段落功能。 你可以通过将对象的 IsOptimalParagraphEnabled 属性设置为 true 启用此功能。 但是,启用此功能会影响应用程序的性能。 除非需要,否则不建议使用最佳段落功能。

另请参阅