パフォーマンスの最適化:テキスト

WPF には、機能豊富なユーザー インターフェイス (UI) コントロールを使用した、テキスト コンテンツ表示のサポートが含まれています。 一般にテキスト レンダリングは 3 つの階層に分けることができます。

  1. Glyphs オブジェクトと GlyphRun オブジェクトを直接使用する。

  2. FormattedText オブジェクトを使用する。

  3. TextBlock オブジェクトや FlowDocument オブジェクトなどの高レベルのコントロールを使用する。

このトピックでは、テキスト レンダリングのパフォーマンスに関する推奨事項を説明します。

グリフ レベルでのテキスト レンダリング

Windows Presentation Foundation (WPF) は、書式設定後のテキストをインターセプトして保存したいユーザーのために、Glyphs に直接アクセスするグリフ レベルのマークアップなど、高度なテキスト サポートを提供しています。 これらの機能は、下記のようなシナリオでのさまざまなテキスト レンダリング要件をサポートする不可欠なものです。

  • 固定形式のドキュメントの画面表示。

  • 印刷シナリオ。

    • デバイス プリンター言語としての Extensible Application Markup Language (XAML)。

    • Microsoft XPS Document Writer。

    • 以前のプリンター ドライバー、Win32 アプリケーションから固定形式への出力。

    • 印刷スプール形式。

  • 以前のバージョンの Windows のクライアントおよびその他のコンピューティング デバイスを含む、固定形式のドキュメント表示。

注意

Glyphs および GlyphRun は、固定形式のドキュメント表示と印刷シナリオ向けに設計されています。 WPF には、一般的なレイアウトと、LabelTextBlock などのユーザー インターフェイス (UI) シナリオのための要素が用意されています。 レイアウトと UI シナリオの詳細については、「WPF のタイポグラフィ」を参照してください。

次の例は、XAML の Glyphs オブジェクトのプロパティを定義する方法を示しています。 この例では、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) でテキストをレンダリングする最も効率的な方法は、GlyphsGlyphRun を使用して、グリフ レベルでテキスト コンテンツを生成することです。 ただし、この効率の代償として、TextBlockFlowDocument など、Windows Presentation Foundation (WPF) コントロールの組み込み機能である使いやすいリッチ テキストの書式設定が失われます。

FormattedText オブジェクト

FormattedText オブジェクトを使用すると、複数行のテキストを描画できます。このテキストでは、テキスト内の各文字を個々に書式設定できます。 詳細については、「書式設定されたテキストの描画」を参照してください。

書式設定されたテキストを作成するには、FormattedText コンストラクターを呼び出して FormattedText オブジェクトを作成します。 最初の書式設定済みテキスト文字列を作成したら、書式スタイルの範囲を適用できます。 アプリケーションで独自のレイアウトを実装する場合は、TextBlock などのコントロールを使用するよりも、FormattedTextオブジェクトの方が適しています。 FormattedText オブジェクトの詳細については、「書式設定されたテキストの描画」を参照してください。

FormattedText オブジェクトには、低レベルのテキスト書式設定機能が用意されています。 複数の書式スタイルを 1 つ以上の文字に適用できます。 たとえば、SetFontSize メソッドと SetForegroundBrush メソッドの両方を呼び出して、テキストの最初の 5 文字の書式設定を変更できます。

次のコード例では、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、ラベル コントロール

WPF には画面にテキストを描画するための複数のコントロールが含まれています。 各コントロールは異なるシナリオを対象にしており、それぞれに一連の機能と制限があります。

FlowDocument は TextBlock やラベルよりもパフォーマンスへの影響が大きい

一般的に、ユーザー インターフェイス (UI) で短い文を使用するなど、限定的なテキストのサポートが必要な場合は、TextBlock 要素を使用する必要があります。 最小限のテキスト サポートが必要な場合には、Label を使用します。 FlowDocument 要素は、コンテンツのリッチな表示をサポートする再フロー可能なドキュメントのコンテナーなので、TextBlock コントロールや Label コントロールを使用するのに比べてパフォーマンスに大きく影響します。

FlowDocument の詳細については、「フロー ドキュメントの概要」を参照してください。

FlowDocument での TextBlock の使用を避ける

TextBlock 要素は UIElement から派生します。 Run 要素は TextElement から派生します。これは、UIElement 派生オブジェクトよりも使用コストが低くなります。 テキスト コンテンツを FlowDocument で表示する場合は、可能であれば TextBlock ではなく Run を使用します。

次のマークアップ サンプルは、FlowDocument 内でテキスト コンテンツを設定する 2 つの方法を示しています。

<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 は使用しない

一般に、TextBlock 内で Run を使用すると、明示的な Run オブジェクトをまったく使用しない場合よりもパフォーマンス負荷が高くなります。 テキストのプロパティを設定するために Run を使用している場合は、代わりにそれらのプロパティを TextBlock に直接設定してください。

次のマークアップ サンプルは、テキスト プロパティ (この例では FontWeight プロパティ) を設定するこれらの 2 つの方法を示しています。

<!-- 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 プロパティへのデータ バインドを避ける

String ソースから頻繁に更新される Label オブジェクトがあるシナリオを考えてみましょう。 Label 要素の Content プロパティを String ソース オブジェクトにデータ バインドすると、パフォーマンスが低下する可能性があります。 ソース String が更新されるたびに、古い String オブジェクトは破棄され、新しい String が再作成されます。String オブジェクトは不変であるため、変更できません。 その結果、Label オブジェクトの ContentPresenter によって古いコンテンツは破棄され、新しいコンテンツが再生成され、新しい String が表示されます。

この問題の解決策は単純です。 Label がカスタムの ContentTemplate 値に設定されていない場合は、LabelTextBlock に置き換え、その Text プロパティをソース文字列にデータ バインドします。

データ バインドされたプロパティ 更新時間 (ミリ秒)
Label.Content 835
TextBlock.Text 242

Hyperlink オブジェクトは、インライン レベルのフロー コンテンツ要素であり、フロー コンテンツ内のハイパーリンクをホストすることができます。

複数の Hyperlink要素の使用を最適化するには、同じ TextBlock 内にグループ化します。 こうすると、アプリケーション内で作成するオブジェクトの数を最小限に抑えるのに役立ちます。 たとえば、次のように複数のハイパーリンクを表示するとします。

MSN Home |My 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>

次のマークアップの例は、ハイパーリンクをより効率的に表示する方法を示しています。今回は 1 つの 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 は、自動ハイフネーションなどのリッチ テキスト書式設定サービスを提供しています。 これらのサービスはアプリケーションのパフォーマンスに影響を与える可能性があり、必要な場合のみ使用すべきです。

ハイフネーションの不要な使用を避ける

自動ハイフネーションを使用すると、テキスト行のハイフン ブレークポイントを検出し、TextBlock オブジェクトと FlowDocument オブジェクトの行にブレーク位置を追加できます。 既定では、これらのオブジェクトでは自動ハイフネーション機能は無効です。 この機能は、オブジェクトの IsHyphenationEnabled プロパティを true に設定することで有効にできます。 ただし、この機能を有効にすると、WPF によってコンポーネント オブジェクト モデル (COM) の相互運用機能が開始されます。これは、アプリケーションのパフォーマンスに影響する可能性があります。 必要でない限り、自動ハイフネーションを使用しないことをお勧めします。

図表を慎重に使用する

Figure 要素は、コンテンツのページ内に絶対的に配置できるフロー コンテンツの一部を表します。 位置が既にレイアウトされているコンテンツと競合する場合、Figure によってページ全体が自動的に再フォーマットされることがあります。不要な再フォーマットの可能性を最小限に抑えるには、隣り合う Figure 要素をグループ化するか、固定ページ サイズの場合はコンテンツの最上部で宣言します。

適切な段落

FlowDocument オブジェクトの適切な段落の機能は、空白ができる限り均等に分散されるように段落をレイアウトします。 既定では、適切な段落の機能は無効です。 この機能を有効にするには、オブジェクトの IsOptimalParagraphEnabled プロパティを true に設定します。 ただし、この機能を有効にするとアプリケーションのパフォーマンスに影響します。 必要でない限り、適切な段落の機能を使用しないことをお勧めします。

関連項目