Optimizing Performance: Text

WPF includes support for the presentation of text content through the use of feature-rich user interface (UI) controls. In general you can divide text rendering in three layers:

  1. Using the Glyphs and GlyphRun objects directly.

  2. Using the FormattedText object.

  3. Using high-level controls, such as the TextBlock and FlowDocument objects.

This topic provides text rendering performance recommendations.

Rendering Text at the Glyph Level

Windows Presentation Foundation (WPF) provides advanced text support including glyph-level markup with direct access to Glyphs for customers who want to intercept and persist text after formatting. These features provide critical support for the different text rendering requirements in each of the following scenarios.

  • Screen display of fixed-format documents.

  • Print scenarios.

    • Extensible Application Markup Language (XAML) as a device printer language.

    • Microsoft XPS Document Writer.

    • Previous printer drivers, output from Win32 applications to the fixed format.

    • Print spool format.

  • Fixed-format document representation, including clients for previous versions of Windows and other computing devices.

Note

Glyphs and GlyphRun are designed for fixed-format document presentation and print scenarios. WPF provides several elements for general layout and user interface (UI) scenarios such as Label and TextBlock. For more information on layout and UI scenarios, see the Typography in WPF.

The following examples show how to define properties for a Glyphs object in XAML. The examples assume that the Arial, Courier New, and Times New Roman fonts are installed in the C:\WINDOWS\Fonts folder on the local computer.

<!-- 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>

Using DrawGlyphRun

If you have custom control and you want to render glyphs, use the DrawGlyphRun method.

WPF also provides lower-level services for custom text formatting through the use of the FormattedText object. The most efficient way of rendering text in Windows Presentation Foundation (WPF) is by generating text content at the glyph level using Glyphs and GlyphRun. However, the cost of this efficiency is the loss of easy to use rich text formatting, which are built-in features of Windows Presentation Foundation (WPF) controls, such as TextBlock and FlowDocument.

FormattedText Object

The FormattedText object allows you to draw multi-line text, in which each character in the text can be individually formatted. For more information, see Drawing Formatted Text.

To create formatted text, call the FormattedText constructor to create a FormattedText object. Once you have created the initial formatted text string, you can apply a range of formatting styles. If your application wants to implement its own layout, then the FormattedText object is better choice than using a control, such as TextBlock. For more information on the FormattedText object, see Drawing Formatted Text .

The FormattedText object provides low-level text formatting capability. You can apply multiple formatting styles to one or more characters. For example, you could call both the SetFontSize and SetForegroundBrush methods to change the formatting of the first five characters in the text.

The following code example creates a FormattedText object and renders it.

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, and Label Controls

WPF includes multiple controls for drawing text to the screen. Each control is targeted to a different scenario and has its own list of features and limitations.

FlowDocument Impacts Performance More than TextBlock or Label

In general, the TextBlock element should be used when limited text support is required, such as a brief sentence in a user interface (UI). Label can be used when minimal text support is required. The FlowDocument element is a container for re-flowable documents that support rich presentation of content, and therefore, has a greater performance impact than using the TextBlock or Label controls.

For more information on FlowDocument, see Flow Document Overview.

Avoid Using TextBlock in FlowDocument

The TextBlock element is derived from UIElement. The Run element is derived from TextElement, which is less costly to use than a UIElement-derived object. When possible, use Run rather than TextBlock for displaying text content in a FlowDocument.

The following markup sample illustrates two ways of setting text content within a 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>

Avoid Using Run to Set Text Properties

In general, using a Run within a TextBlock is more performance intensive than not using an explicit Run object at all. If you are using a Run in order to set text properties, set those properties directly on the TextBlock instead.

The following markup sample illustrates these two ways of setting a text property, in this case, the FontWeight property:

<!-- 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>

The following table shows the cost of displaying 1000 TextBlock objects with and without an explicit Run.

TextBlock type Creation time (ms) Render time (ms)
Run setting text properties 146 540
TextBlock setting text properties 43 453

Avoid Databinding to the Label.Content Property

Imagine a scenario where you have a Label object that is updated frequently from a String source. When data binding the Label element's Content property to the String source object, you may experience poor performance. Each time the source String is updated, the old String object is discarded and a new String is recreated—because a String object is immutable, it cannot be modified. This, in turn, causes the ContentPresenter of the Label object to discard its old content and regenerate the new content to display the new String.

The solution to this problem is simple. If the Label is not set to a custom ContentTemplate value, replace the Label with a TextBlock and data bind its Text property to the source string.

Data bound property Update time (ms)
Label.Content 835
TextBlock.Text 242

The Hyperlink object is an inline-level flow content element that allows you to host hyperlinks within the flow content.

You can optimize the use of multiple Hyperlink elements by grouping them together within the same TextBlock. This helps to minimize the number of objects you create in your application. For example, you may want to display multiple hyperlinks, such as the following:

MSN Home | My MSN

The following markup example shows multiple TextBlock elements used to display the hyperlinks:

<!-- 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>

The following markup example shows a more efficient way of displaying the hyperlinks, this time, using a single 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>

A TextDecoration object is a visual ornamentation that you can add to text; however, it can be performance intensive to instantiate. If you make extensive use of Hyperlink elements, consider showing an underline only when triggering an event, such as the MouseEnter event. For more information, see Specify Whether a Hyperlink is Underlined.

The following image shows how the MouseEnter event triggers the underlined hyperlink:

Hyperlinks displaying TextDecorations

The following markup sample shows a Hyperlink defined with and without an underline:

<!-- 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>

The following table shows the performance cost of displaying 1000 Hyperlink elements with and without an underline.

Hyperlink Creation time (ms) Render time (ms)
With underline 289 1130
Without underline 299 776

Text Formatting Features

WPF provides rich text formatting services, such as automatic hyphenations. These services may impact application performance and should only be used when needed.

Avoid Unnecessary Use of Hyphenation

Automatic hyphenation finds hyphen breakpoints for lines of text, and allows additional break positions for lines in TextBlock and FlowDocument objects. By default, the automatic hyphenation feature is disabled in these objects. You can enable this feature by setting the object's IsHyphenationEnabled property to true. However, enabling this feature causes WPF to initiate Component Object Model (COM) interoperability, which can impact application performance. It is recommended that you do not use automatic hyphenation unless you need it.

Use Figures Carefully

A Figure element represents a portion of flow content that can be absolutely-positioned within a page of content. In some cases, a Figure may cause an entire page to automatically reformat if its position collides with content that has already been laid-out. You can minimize the possibility of unnecessary reformatting by either grouping Figure elements next to each other, or declaring them near the top of content in a fixed page size scenario.

Optimal Paragraph

The optimal paragraph feature of the FlowDocument object lays out paragraphs so that white space is distributed as evenly as possible. By default, the optimal paragraph feature is disabled. You can enable this feature by setting the object's IsOptimalParagraphEnabled property to true. However, enabling this feature impacts application performance. It is recommended that you do not use the optimal paragraph feature unless you need it.

See also