Оптимизация производительности: отображение текста

WPF включает поддержку представления текстового контента с помощью многофункциональных элементов управления пользовательского интерфейса. В целом можно разделить отрисовку текста на три уровня.

  1. Использование объектов Glyphs и GlyphRun напрямую.

  2. Использование объекта FormattedText.

  3. Использование высокоуровневых элементов управления, таких как объекты TextBlock и FlowDocument.

В этом разделе даются рекомендации по повышению производительности отрисовки текста.

Отрисовка текста на уровне глифа

Windows Presentation Foundation (WPF) обеспечивает расширенную поддержку текста, включая разметку на уровне глифа с прямым доступом к Glyphs для клиентов, которым требуется перехватывать и сохранять текст после форматирования. Эти функции обеспечивают критически важную поддержку различных требований к отрисовке текста в каждом из следующих сценариев.

  • Отображение на экране документов фиксированного формата.

  • Сценарии печати.

    • XAML как язык принтера.

    • Средство записи XPS-документов (Майкрософт).

    • Предыдущие драйверы принтера, вывод из приложений Win32 в фиксированный формат.

    • Формат очереди печати.

  • Представление документов фиксированного формата, включая клиенты предыдущих версий Windows и другие вычислительные устройства.

Примечание.

Glyphs и GlyphRun предназначены для использования в сценариях представления и печати документов в фиксированном формате. WPF предоставляет несколько элементов для общих сценариев макета и пользовательского интерфейса, таких как Label и TextBlock. Дополнительные сведения о сценариях макета и пользовательского интерфейса см. в разделе " Типография" в WPF.

В следующих примерах показано определение свойств для объекта Glyphs в XAML. В примерах предполагается, что шрифты Arial, Courier New и Times New Roman устанавливаются в папку C:\WINDOWS\Fonts на локальном компьютере.

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

WPF также предоставляет службы более низкого уровня для пользовательского форматирования текста с помощью объекта FormattedText. Наиболее эффективным способом отрисовки текста в Windows Presentation Foundation (WPF) является создание текстового контента на уровне глифа с помощью Glyphs и GlyphRun. Однако ценой этой эффективности будет потеря простого в использовании форматирования RTF, которое поддерживается встроенными функциями элементов управления Windows Presentation Foundation (WPF), таких как TextBlock и FlowDocument.

Объект FormattedText

Объект FormattedText позволяет рисовать многострочный текст, в котором каждый символ в текст можно форматировать отдельно. Дополнительные сведения см. в разделе Рисование форматированного текста.

При создании форматированного текста используйте конструктор FormattedText для создания объекта FormattedText. После создания исходной строки форматированного текста можно применить ряд стилей форматирования. Если в приложении требуется реализовать собственный макет, то рекомендуется использовать объект FormattedText, а не элемент управления, такой как TextBlock. Дополнительные сведения об объекте FormattedText см. в разделе Рисование форматированного текста.

Объект FormattedText предоставляет возможность низкоуровневого форматирования текста. К одному или более символам можно применить несколько стилей форматирования. Например, можно вызвать методы SetFontSize и SetForegroundBrush, чтобы изменить форматирование первых пяти символов в тексте.

В следующем примере кода создается и преобразуется для просмотра объект 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 следует использовать, если требуется ограниченная поддержка текста, например короткого предложения в пользовательском интерфейсе. Label можно использовать, если требуется минимальная поддержка текста. Элемент FlowDocument является контейнером для документов плавающей верстки, поддерживающих представление форматированного контента, и, следовательно, он оказывает большее влияние на производительность, чем использование элементов управления TextBlock или Label.

Дополнительные сведения о FlowDocument см. в разделе Общие сведения о документах нефиксированного формата.

Избегайте использования TextBlock в FlowDocument

Элемент TextBlock является производным от UIElement. Элемент Run является производным от 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>

В следующей таблице показана стоимость отображения 1000 объектов TextBlock с помощью и без помощи явного Run.

Тип TextBlock Время создания (мс) Время отрисовки (мс)
Установка свойств текста с помощью Run 146 540
Установка свойств текста с помощью TextBlock 43 453

Избегайте привязки данных к свойству Label.Content

Представьте ситуацию, в которой имеется объект Label, часто обновляемый из источника String. При выполнении привязки данных свойства Content элемента Label к исходному объекту String может наблюдаться низкая производительность. При каждом обновлении источника String старый объект String удаляется и создается новый объект String — поскольку String является неизменяемым, его нельзя изменить. Это, в свою очередь, приводит к тому, что ContentPresenter объекта Label удаляет свое устаревшее содержимое и создает новое содержимое для отображения нового объекта String.

Решение этой проблемы довольно простое. Если для объекта Label не задано пользовательское значение ContentTemplate, замените Label на TextBlock и привяжите его свойство Text к данным к исходной строке.

Свойство с привязкой данных Время обновления (мс)
Label.Content 835
TextBlock.Text 242

Объект Hyperlink является элементом размещения содержимого встроенного уровня, позволяющим размещать гиперссылки в содержимом нефиксированного формата.

Вы можете оптимизировать использование нескольких элементов Hyperlink объединив их вместе в одном TextBlock. Это позволяет минимизировать число объектов, создаваемых в приложении. Например, может потребоваться отобразить несколько гиперссылок, как показано ниже.

Домашняя страница 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 с подчеркиванием и без него.

Гиперссылка Время создания (мс) Время отрисовки (мс)
С подчеркиванием 289 1130
Без подчеркивания 299 776

Возможности форматирования текста

WPF предоставляет службы форматирования RTF, такие как автоматическая расстановка переносов. Эти службы могут влиять на производительность приложения и должны использоваться только при необходимости.

Избегайте излишнего использования расстановки переносов

Автоматическая расстановка переносов находит точки вставки дефиса для строк текста и разрешает дополнительные позиции разрыва для строк в объектах TextBlock и FlowDocument. По умолчанию возможность автоматического переноса в этих объектах отключена. Эту функцию можно включить, установив свойство IsHyphenationEnabled объекта в значение true. Однако включение этой функции заставляет WPF инициировать взаимодействие модели COM, которое может повлиять на производительность приложения. Рекомендуется не использовать автоматическую расстановку переносов, если это не является обязательным.

Внимательно используйте рисунки

Элемент Figure представляет часть содержимого нефиксированного формата, которое может быть абсолютно позиционировано внутри страницы содержимого. В некоторых случаях Figure может вызывать автоматическое переформатирование всей страницы, если его расположение конфликтует с уже размещенным содержимым. Чтобы свести к минимуму вероятность ненужного переформатирования, можно либо сгруппировать элементы Figure рядом друг с другом, либо объявить их в верхней части содержимого в случае фиксированного размера страницы.

Оптимальный абзац

Функция оптимального абзаца объекта FlowDocument располагает абзацы так, чтобы пустое пространство распределялось как можно более равномерно. По умолчанию средство оптимального абзаца отключено. Чтобы включить эту функцию, необходимо задать для свойства IsOptimalParagraphEnabled объекта значение true. Однако включение этой функции влияет на производительность приложения. Рекомендуется не использовать средство оптимального абзаца без необходимости.

См. также