次の方法で共有


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

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

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

  2. FormattedText オブジェクトの使用。

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

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

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

Windows Presentation Foundation (WPF) は、グリフ レベルのマークアップを含む高度なテキスト サポートを提供し、書式設定後にテキストをインターセプトして保持するユーザー向けに、Glyphs に直接アクセスできます。 これらの機能は、次の各シナリオのさまざまなテキスト レンダリング要件に対して重要なサポートを提供します。

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

  • 印刷シナリオ。

    • デバイス プリンター言語としての拡張可能なアプリケーション マークアップ言語 (XAML)。

    • Microsoft XPS ドキュメント ライター。

    • 以前のプリンタードライバーは、Win32アプリケーションから固定形式に出力されました。

    • 印刷スプール形式。

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

GlyphsGlyphRun は、固定形式のドキュメントのプレゼンテーションと印刷のシナリオ向けに設計されています。 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 オブジェクトを作成します。 最初の書式設定されたテキスト文字列を作成したら、書式設定スタイルの範囲を適用できます。 アプリケーションで独自のレイアウトを実装する場合、 FormattedText オブジェクトは、 TextBlockなどのコントロールを使用するよりも適しています。 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 またはラベルよりもパフォーマンスに影響を与える

一般に、TextBlock 要素は、ユーザー インターフェイス (UI) の簡単な文など、限られたテキストサポートが必要な場合に使用する必要があります。 Label は、最小限のテキストサポートが必要な場合に使用できます。 FlowDocument要素は、コンテンツの豊富なプレゼンテーションをサポートする再フロー可能なドキュメントのコンテナーであるため、TextBlockまたはLabelコントロールを使用するよりもパフォーマンスに大きな影響を与えます。

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

FlowDocument で TextBlock を使用しないようにする

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

次のマークアップ サンプルは、 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 を使用している場合は、代わりにそれらのプロパティを 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>

次の表は、明示的なTextBlockの有無にかかわらず、1,000 個のRun オブジェクトを表示するコストを示しています。

TextBlock の種類 作成時間 (ミリ秒) レンダリング時間 (ミリ秒)
テキスト プロパティの設定を実行する 146 540
TextBlock のテキスト プロパティの設定 43 453

Label.Content プロパティへのデータ バインドを回避する

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

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

データ バインド プロパティ 更新時刻 (ミリ秒)
ラベル.コンテンツ 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>

次のマークアップの例は、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 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 に設定します。 ただし、この機能を有効にすると、アプリケーションのパフォーマンスに影響します。 必要な場合を除き、最適な段落機能は使用しないことをお勧めします。

こちらも参照ください