次の方法で共有


高度なテキストの書式設定

Windows Presentation Foundation (WPF) には、アプリケーションにテキストを含める堅牢な API セットが用意されています。 TextBlockなどのレイアウトおよびユーザー インターフェイス (UI) API は、テキスト表示に最も一般的で一般的な要素を提供します。 GlyphRunDrawingFormattedTextなどの描画 API は、書式設定されたテキストを図面に含める手段を提供します。 最も高度なレベルでは、WPF は、テキスト ストア管理、テキスト実行の書式設定管理、埋め込みオブジェクト管理など、テキスト プレゼンテーションのあらゆる側面を制御する拡張可能なテキスト書式設定エンジンを提供します。

このトピックでは、WPF テキストの書式設定の概要について説明します。 ここでは、クライアントの実装と WPF テキスト書式設定エンジンの使用に重点を置いています。

手記

このドキュメント内のすべてのコード例は、テキスト書式設定のサンプルにあります。

前提 条件

このトピックでは、テキスト表示に使用される上位レベルの API について理解していることを前提としています。 ほとんどのユーザー シナリオでは、このトピックで説明する高度なテキスト書式設定 API は必要ありません。 さまざまなテキスト API の概要については、「WPFの ドキュメント」を参照してください。

高度なテキストの書式設定

WPF のテキスト レイアウトと UI コントロールには、書式設定されたテキストをアプリケーションに簡単に含める書式設定プロパティが用意されています。 これらのコントロールは、テキストの表示を処理するさまざまなプロパティを公開します。これには、そのタイプフェイス、サイズ、色が含まれます。 通常の状況では、これらのコントロールは、アプリケーション内のテキストプレゼンテーションの大部分を処理できます。 ただし、一部の高度なシナリオでは、テキストストレージとテキストプレゼンテーションの制御が必要です。 WPF は、この目的のために拡張可能なテキスト書式設定エンジンを提供します。

WPF の高度なテキスト書式設定機能は、テキスト書式設定エンジン、テキスト ストア、テキスト実行、および書式設定プロパティで構成されます。 テキスト書式設定エンジン (TextFormatter) は、プレゼンテーションに使用するテキスト行を作成します。 これは、行の書式設定プロセスを開始し、テキスト フォーマッタの FormatLineを呼び出すことによって実現されます。 テキスト フォーマッタでは、テキスト ストアの GetTextRun メソッドを呼び出すことによって、ストアからテキスト ランが取得されます。 TextRun オブジェクトは、テキスト フォーマッタによって TextLine オブジェクトに形成され、検査または表示のためにアプリケーションに渡されます。

テキスト フォーマッタの使用

TextFormatter は WPF テキスト書式設定エンジンであり、テキスト行の書式設定と改行を行うサービスを提供します。 テキスト フォーマッタは、さまざまなテキスト文字形式と段落スタイルを処理でき、国際テキスト レイアウトのサポートが含まれています。

従来のテキスト API とは異なり、TextFormatter は一連のコールバック メソッドを介してテキスト レイアウト クライアントと対話します。 クライアントは、TextSource クラスの実装でこれらのメソッドを提供する必要があります。 次の図は、クライアント アプリケーションと TextFormatterの間のテキスト レイアウトの相互作用を示しています。

テキスト レイアウト クライアントと TextFormatter の図

テキスト フォーマッタは、TextSourceの実装であるテキスト ストアから書式設定されたテキスト行を取得するために使用されます。 これを行うには、まず、Create メソッドを使用してテキスト フォーマッタのインスタンスを作成します。 このメソッドは、テキスト フォーマッタのインスタンスを作成し、最大行の高さと幅の値を設定します。 テキスト フォーマッタのインスタンスが作成されるとすぐに、FormatLine メソッドを呼び出して行の作成プロセスが開始されます。 TextFormatter では、テキスト ソースが再び呼び出されて、行を形成するテキスト ランのテキストと書式設定パラメーターが取得されます。

次の例では、テキスト ストアを書式設定するプロセスを示します。 TextFormatter オブジェクトは、テキスト ストアからテキスト行を取得し、DrawingContextに描画するテキスト行の書式を設定するために使用されます。

// Create a DrawingGroup object for storing formatted text.
textDest = new DrawingGroup();
DrawingContext dc = textDest.Open();

// Update the text store.
_textStore.Text = textToFormat.Text;
_textStore.FontRendering = _currentRendering;

// Create a TextFormatter object.
TextFormatter formatter = TextFormatter.Create();

// Format each line of text from the text store and draw it.
while (textStorePosition < _textStore.Text.Length)
{
   // Create a textline from the text store using the TextFormatter object.
   using (TextLine myTextLine = formatter.FormatLine(
       _textStore,
       textStorePosition,
       96*6,
       new GenericTextParagraphProperties(_currentRendering),
       null))
   {
       // Draw the formatted text into the drawing context.
       myTextLine.Draw(dc, linePosition, InvertAxes.None);

       // Update the index position in the text store.
       textStorePosition += myTextLine.Length;

       // Update the line position coordinate for the displayed line.
       linePosition.Y += myTextLine.Height;
   }
}

// Persist the drawn text content.
dc.Close();

// Display the formatted text in the DrawingGroup object.
myDrawingBrush.Drawing = textDest;
' Create a DrawingGroup object for storing formatted text.
textDest = New DrawingGroup()
Dim dc As DrawingContext = textDest.Open()

' Update the text store.
_textStore.Text = textToFormat.Text
_textStore.FontRendering = _currentRendering

' Create a TextFormatter object.
Dim formatter As TextFormatter = TextFormatter.Create()

' Format each line of text from the text store and draw it.
Do While textStorePosition < _textStore.Text.Length
   ' Create a textline from the text store using the TextFormatter object.
   Using myTextLine As TextLine = formatter.FormatLine(_textStore, textStorePosition, 96*6, New GenericTextParagraphProperties(_currentRendering), Nothing)
       ' Draw the formatted text into the drawing context.
       myTextLine.Draw(dc, linePosition, InvertAxes.None)

       ' Update the index position in the text store.
       textStorePosition += myTextLine.Length

       ' Update the line position coordinate for the displayed line.
       linePosition.Y += myTextLine.Height
   End Using
Loop

' Persist the drawn text content.
dc.Close()

' Display the formatted text in the DrawingGroup object.
myDrawingBrush.Drawing = textDest

クライアント テキスト ストアの実装

テキスト書式設定エンジンを拡張する場合は、テキスト ストアのすべての側面を実装して管理する必要があります。 これは簡単な作業ではありません。 テキスト ストアは、テキスト実行プロパティ、段落プロパティ、埋め込みオブジェクト、およびその他の同様のコンテンツを追跡します。 また、テキスト フォーマッタには、テキスト フォーマッタが TextLine オブジェクトの作成に使用する個々の TextRun オブジェクトも用意されています。

テキスト ストアの仮想化を処理するには、テキスト ストアを TextSourceから派生させる必要があります。 TextSource は、テキスト フォーマッタがテキスト ストアからテキスト実行を取得するために使用するメソッドを定義します。 テキスト フォーマッタが行の書式設定で使用するテキスト ランを取得するためのメソッドが GetTextRun です。 GetTextRun の呼び出しは、次のいずれかの条件が発生するまで、テキスト フォーマッタによって繰り返し行われます。

  • TextEndOfLine またはサブクラスが返されます。

  • テキスト実行の累積幅が、テキスト フォーマッタを作成する呼び出しまたはテキスト フォーマッタの FormatLine メソッドの呼び出しで指定された最大行幅を超えています。

  • "CF"、"LF"、"CRLF" などの Unicode 改行シーケンスが返されます。

テキスト ランを提供する

テキスト書式設定プロセスの中核となるのは、テキスト フォーマッタとテキスト ストアの間の相互作用です。 TextSource の実装により、テキスト フォーマッタに TextRun オブジェクトと、テキスト実行の書式設定に使用するプロパティが提供されます。 この相互作用は、テキスト フォーマッタによって呼び出される GetTextRun メソッドによって処理されます。

次の表は、定義済みの TextRun オブジェクトの一部を示しています。

TextRun の種類 使用方法
TextCharacters 文字グリフの表現をテキスト フォーマッタに戻すために使用される特殊なテキストラン。
TextEmbeddedObject テキスト内のボタンや画像など、測定、ヒット テスト、描画が全体で行われるコンテンツを提供するために使用される特殊なテキストラン。
TextEndOfLine 行の末尾をマークするために使用される特殊なテキストラン。
TextEndOfParagraph 段落の末尾をマークするために使用される特殊なテキストラン。
TextEndOfSegment 前の TextModifier 実行の影響を受けるスコープを終了するなど、セグメントの末尾をマークするために使用される特殊なテキスト実行。
TextHidden 隠し文字の範囲をマークするために使用される特殊なテキストラン。
TextModifier その範囲でテキスト ランのプロパティを変更するための特殊なテキスト ラン。 スコープは、次に一致する TextEndOfSegment テキストラン、または次の TextEndOfParagraphまで拡張されます。

定義済みの TextRun オブジェクトは、いずれもサブクラス化できます。 これにより、テキスト ソースは、カスタム データを含むテキスト ランをテキスト フォーマッタに提供できます。

次の例では、GetTextRun メソッドを示します。 このテキスト ストアは、処理のために TextRun オブジェクトをテキスト フォーマッタに返します。

// Used by the TextFormatter object to retrieve a run of text from the text source.
public override TextRun GetTextRun(int textSourceCharacterIndex)
{
   // Make sure text source index is in bounds.
   if (textSourceCharacterIndex < 0)
      throw new ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.");
   if (textSourceCharacterIndex >= _text.Length)
   {
      return new TextEndOfParagraph(1);
   }

   // Create TextCharacters using the current font rendering properties.
   if (textSourceCharacterIndex < _text.Length)
   {
      return new TextCharacters(
         _text,
         textSourceCharacterIndex,
         _text.Length - textSourceCharacterIndex,
         new GenericTextRunProperties(_currentRendering));
   }

   // Return an end-of-paragraph if no more text source.
   return new TextEndOfParagraph(1);
}
' Used by the TextFormatter object to retrieve a run of text from the text source.
Public Overrides Function GetTextRun(ByVal textSourceCharacterIndex As Integer) As TextRun
   ' Make sure text source index is in bounds.
   If textSourceCharacterIndex < 0 Then
      Throw New ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.")
   End If
   If textSourceCharacterIndex >= _text.Length Then
      Return New TextEndOfParagraph(1)
   End If

   ' Create TextCharacters using the current font rendering properties.
   If textSourceCharacterIndex < _text.Length Then
      Return New TextCharacters(_text, textSourceCharacterIndex, _text.Length - textSourceCharacterIndex, New GenericTextRunProperties(_currentRendering))
   End If

   ' Return an end-of-paragraph if no more text source.
   Return New TextEndOfParagraph(1)
End Function

手記

この例では、テキスト ストアは、すべてのテキストに同じテキスト プロパティを提供します。 高度なテキスト ストアでは、個々の文字が異なるプロパティを持つことができるように、独自のスパン管理を実装する必要があります。

書式設定プロパティの指定

TextRun オブジェクトは、テキスト ストアによって提供されるプロパティを使用して書式設定されます。 これらのプロパティには、TextParagraphPropertiesTextRunPropertiesの 2 種類があります。 TextParagraphPropertiesTextAlignmentFlowDirectionなどの段落を含むプロパティを処理します。 TextRunProperties は、前景ブラシ、Typeface、フォント サイズなど、段落内で実行されるテキストごとに異なるプロパティです。 カスタムの段落プロパティ型とカスタム テキスト実行プロパティ型を実装するには、アプリケーションで、それぞれ TextParagraphProperties および TextRunProperties から派生するクラスを作成する必要があります。

関連項目