优化性能:文本

WPF 支持通过使用功能丰富的user interface (UI) 控件来表示文本内容。 通常,您可以将文本呈现分为三层:

  1. 直接使用 GlyphsGlyphRun 对象。

  2. 使用 FormattedText 对象。

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

本主题提供了一些针对文本呈现性能的建议。

本主题包括下列各节。

  • 在标志符号级别呈现文本
  • FormattedText 对象
  • FlowDocument、TextBlock 和 Label 控件
  • Hyperlink
  • 文本格式设置功能
  • 相关主题

在标志符号级别呈现文本

Windows Presentation Foundation (WPF) 为想要在设置文本格式之后截取和保存文本的客户提供了高级文本支持,包括可直接访问 Glyphs 的标志符号级别标记。 这些功能为下列每一种方案中的不同文本呈现要求提供了关键支持。

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

  • 打印方案。

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

    • Microsoft XPS Document Writer.

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

    • 打印后台格式。

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

注意注意

GlyphsGlyphRun 设计用于固定格式的文档表示和打印方案。Windows Presentation Foundation (WPF) 为常规布局和 user interface (UI) 方案(例如 LabelTextBlock)提供了若干元素。有关布局和 UI 方案的更多信息,请参见 WPF 中的版式

下面的示例演示如何为Extensible Application Markup Language (XAML) 中的 Glyphs 对象定义属性。 Glyphs 对象表示 XAML 中的 GlyphRun 的输出。 这些示例假设本地计算机上的 C:\WINDOWS\Fonts 文件夹中安装了 Arial、Courier New 和 Times New Roman 字体。

<!-- The example shows how to use a Glyphs object. -->
<Page
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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 方法。

WPF 还通过使用 FormattedText 对象,为自定义文本格式设置提供了低级别服务。 在 Windows Presentation Foundation (WPF) 中,呈现文本最有效的方法是使用 GlyphsGlyphRun 在标志符号级别生成文本内容。 但是,这种有效性的代价是增加了使用 RTF 文本格式设置的难度。RTF 文本格式设置是 Windows Presentation Foundation (WPF) 控件(例如 TextBlockFlowDocument)的内置功能。

FormattedText 对象

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

若要创建格式化文本,请调用 FormattedText 构造函数以创建一个 FormattedText 对象。 一旦创建了设置了格式的初始文本字符串,便可以应用某一范围的格式样式。 如果您的应用程序想要实现其自己的布局,则使用 FormattedText 对象比使用控件(例如 TextBlock)更好。 有关 FormattedText 对象的更多信息,请参见 绘制格式化文本

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

下面的代码示例创建一个 FormattedText 对象并呈现该对象。

        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
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));
}

FlowDocument、TextBlock 和 Label 控件

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

FlowDocument 比 TextBlock 或 Label 更能影响性能

通常,当需要支持的文本有限(例如user interface (UI) 中的简短语句)时,应使用 TextBlock 元素。 当需要支持文本极少时,可以使用 LabelFlowDocument 元素是支持内容丰富表示的可重流动文档的容器,因此,使用该元素时对性能的影响大于使用 TextBlockLabel 控件时对性能的影响。

有关 FlowDocument 的更多信息,请参见 流文档概述

避免在 FlowDocument 中使用 TextBlock

TextBlock 元素派生于 UIElementRun 元素派生于 TextElement,其使用成本低于从 UIElement 派生的对象的使用成本。 如果可能,应使用 Run 而不是 TextBlock 来显示 FlowDocument 中的文本内容。

下面的标记示例阐释了在 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>

下表显示了使用和不使用显式 Run 来显示 1000 个 TextBlock 对象的成本。

TextBlock 类型

创建时间(毫秒)

呈现时间(毫秒)

Run 设置文本属性

146

540

TextBlock 设置文本属性

43

453

避免执行到 Label.Content 属性的数据绑定

假设以下情况:您有一个 Label 对象,该对象频繁从 String 源进行更新。 执行从 Label 元素的 Content 属性到 String 源对象的数据绑定时,性能可能会较差。 每次更新源 String 时,将丢弃旧的 String 对象,并重新创建一个新的 String,这是因为 String 对象是不可变的,无法进行修改。 而该行为会导致 Label 对象的 ContentPresenter 丢弃其旧内容,并重新生成新内容以显示新的 String

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

数据绑定属性

更新时间(毫秒)

Label.Content

835

TextBlock.Text

242

Hyperlink 对象是一个内联级别的流内容元素,允许您在流内容中承载超链接。

将超链接合并在一个 TextBlock 对象中

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

MSN 主页 | 我的 MSN

下面的标记示例演示了用于显示这些超链接的多个 TextBlock 元素:

<!-- Hyperlinks in separate TextBlocks. -->
<TextBlock>
  <Hyperlink TextDecorations="None" NavigateUri="https://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="https://www.msn.com">MSN Home</Hyperlink>

  <Run Text=" | " />

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

仅在触发 MouseEnter 事件时才在超链接上显示下划线

TextDecoration 是一个可以添加到文本中用于视觉装饰的对象;不过,对其进行实例化时可能会很占用资源。 如果您使用大量 Hyperlink 元素,应考虑仅在触发某个事件(如 MouseEnter 事件)时才显示下划线。 有关更多信息,请参见如何:将文本修饰用于超链接

MouseEnter 中显示的超链接

显示 TextDecoration 的超链接

下面的标记示例演示了一个使用和未使用下划线定义的 Hyperlink

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

<Run Text=" | " />

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

下表显示了显示 1000 个使用和不使用下划线的 Hyperlink 元素时的性能开销。

Hyperlink

创建时间(毫秒)

呈现时间(毫秒)

带下划线

289

1130

不带下划线

299

776

文本格式设置功能

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

避免不必要地使用断字

自动断字功能会查找文本行的连字符断点,并允许 TextBlockFlowDocument 对象中的行具有附加断点位置。 默认情况下,这些对象中禁用自动断字功能。 您可以通过将对象的 IsHyphenationEnabled 属性设置为 true 来启用该功能。 但是,启用该功能会导致 WPF 启动Component Object Model (COM) 互操作性,从而影响应用程序性能。 建议在不需要的情况下不要使用自动断字功能。

谨慎使用图形

一个 Figure 元素代表可以在某一页内容中绝对定位的流内容的一部分。 在某些情况下,如果某个 Figure 的位置与已经布局的内容冲突,可能会导致整个页面自动重新设置格式。 您可以通过将相互靠近的 Figure 元素组合在一起,或者将它们声明在内容的顶部附近(在固定页大小的情况下),以便将不必要地重新设置格式的可能性降至最低。

最佳段落

FlowDocument 对象的最佳段落功能可按照使空白尽可能均匀分布的方式来设置段落的布局。 默认情况下禁用最佳段落功能。 您可以通过将对象的 IsOptimalParagraphEnabled 属性设置为 true 来启用该功能。 不过,启用该功能会影响应用程序性能, 因此,建议仅在需要的时候才使用最佳段落功能。

请参见

概念

优化 WPF 应用程序性能

规划应用程序性能

优化性能:利用硬件

优化性能:布局和设计

优化性能:二维图形和图像处理

优化性能:对象行为

优化性能:应用程序资源

优化性能:数据绑定

优化性能:其他建议