パフォーマンスの最適化 : テキスト
WPF では、機能豊富なuser interface (UI) コントロールを使用してテキスト コンテンツを表示できます。 一般に、テキスト レンダリングは次の 3 つの層に分けることができます。
FormattedText オブジェクトを使用する。
TextBlock オブジェクトや FlowDocument オブジェクトなどの高レベルのコントロールを使用する。
このトピックでは、テキストのレンダリングに関するパフォーマンスの推奨事項について説明します。
このトピックは、次のセクションで構成されています。
- グリフ レベルのテキスト レンダリング
- FormattedText オブジェクト
- FlowDocument、TextBlock、および Label コントロール
- Hyperlink
- テキスト書式設定機能
- 関連トピック
グリフ レベルのテキスト レンダリング
Windows Presentation Foundation (WPF) は、書式設定後にテキストの途中受信や永続化を行うことができるように、Glyphs に直接アクセスできるグリフ レベルのマークアップなどの高度なテキスト サポートを提供します。 これらの機能によって、次の各シナリオにおけるさまざまなテキスト レンダリングの要件が満たされます。
固定形式ドキュメントの画面表示。
印刷シナリオ。
デバイス プリンター言語としての Extensible Application Markup Language (XAML)。
Microsoft XPS Document Writer.
Win32 アプリケーションから固定形式に出力される以前のプリンター ドライバー。
印刷スプール形式。
前のバージョンの Windows のクライアントやその他のコンピューティング デバイスを含む固定形式のドキュメントの表示。
メモ |
---|
Glyphs および GlyphRun は、固定形式のドキュメントの表示および印刷シナリオのために設計されています。Windows Presentation Foundation (WPF) には、Label や TextBlock などの一般的なレイアウトおよびuser interface (UI) シナリオのための要素がいくつか用意されています。レイアウトと UI シナリオの詳細については、「WPF のタイポグラフィ」を参照してください。 |
Extensible Application Markup Language (XAML) の Glyphs オブジェクトのプロパティを定義する方法を次の例に示します。 Glyphs オブジェクトは、XAML の GlyphRun の出力を表します。 この例は、Arial、Courier New、および Times New Roman の各フォントが、ローカル コンピューターの C:\WINDOWS\Fonts フォルダーにインストールされていることを前提としています。
<!-- The example shows how to use a Glyphs object. -->
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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 を使用してグリフ レベルでテキスト コンテンツを生成する方法です。 ただし、効率性が高い反面、TextBlock や FlowDocument などの Windows Presentation Foundation (WPF) コントロールに組み込まれているような使いやすいリッチ テキスト書式設定機能は利用できません。
FormattedText オブジェクト
FormattedText オブジェクトを使用すると、複数行のテキストを描画できます。このテキストでは、テキスト内の各文字を個々に書式設定できます。 詳細については、「書式設定されたテキストの描画」を参照してください。
書式設定されたテキストを作成するには、FormattedText コンストラクターを呼び出して FormattedText オブジェクトを作成します。 最初の書式設定済みテキスト文字列を作成したら、書式スタイルの範囲を適用できます。 アプリケーションで独自のレイアウトを実装するには、TextBlock などのコントロールを使用するより、FormattedText オブジェクトの方が適しています。 FormattedText オブジェクトの詳細については、「書式設定されたテキストの描画」を参照してください。
FormattedText オブジェクトは、低レベルのテキスト書式設定機能を提供します。 複数の書式スタイルを 1 つ以上の文字に適用できます。 たとえば、SetFontSize メソッドと SetForegroundBrush メソッドの両方を呼び出して、テキストの最初の 5 文字の書式設定を変更できます。
FormattedText オブジェクトを作成して描画するコード例を次に示します。
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
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));
}
FlowDocument、TextBlock、および Label コントロール
WPF には、画面にテキストを描画するための複数のコントロールが含まれています。 各コントロールは、異なるシナリオを対象にしており、それぞれに一連の機能と制限があります。
FlowDocument は TextBlock や Label よりパフォーマンスへの影響が大きい
一般的に、user interface (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>
次の表は、1000 の TextBlock オブジェクトを表示する際に明示的な Run を使用する場合と使用しない場合の負荷の比較を示しています。
TextBlock の種類 |
作成時間 (ミリ秒) |
レンダリング時間 (ミリ秒) |
---|---|---|
Run でテキスト プロパティを設定 |
146 |
540 |
TextBlock でテキスト プロパティを設定 |
43 |
453 |
Label.Content プロパティへのデータ バインディングは使用しない
たとえば、頻繁に String ソースから更新される Label オブジェクトがあったとします。 このような場合、Label 要素の Content プロパティを String ソース オブジェクトにデータ バインドすると、パフォーマンスが低下する可能性があります。 String オブジェクトは不変であり、変更できないため、ソース String が更新されるたびに、古い String オブジェクトが破棄されて新しい String が再作成されます。 その結果、Label オブジェクトの ContentPresenter でも、新しい String を表示するために、古いコンテンツが破棄されて新しいコンテンツが再生成されます。
この問題の解決方法は単純です。 Label がカスタム ContentTemplate 値に設定されていない場合は、Label を TextBlock に置き換えて、その Text プロパティをソース文字列にデータ バインドします。
データ バインドされたプロパティ |
更新時間 (ミリ秒) |
---|---|
Label.Content |
835 |
TextBlock.Text |
242 |
Hyperlink
Hyperlink オブジェクトはインラインレベルのフロー コンテンツ要素であり、これを使用すると、フロー コンテンツ内でハイパーリンクをホストできます。
複数の Hyperlink を 1 つの TextBlock オブジェクトにまとめる
複数の Hyperlink 要素の使用を最適化するには、それらを同じ TextBlock 内にまとめます。 これにより、アプリケーションで作成されるオブジェクトの数を最小限に抑えることができます。 たとえば、次のような複数のハイパーリンクを表示するとします。
MSN Home | My MSN
次のマークアップ例では、複数の TextBlock 要素を使用してこれらのハイパーリンクを表示しています。
<!-- Hyperlinks in separate TextBlocks. -->
<TextBlock>
<Hyperlink TextDecorations="None" NavigateUri="https://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="https://www.msn.com">MSN Home</Hyperlink>
<Run Text=" | " />
<Hyperlink TextDecorations="None" NavigateUri="http://my.msn.com">My MSN</Hyperlink>
</TextBlock>
MouseEnter イベントが発生した場合にのみハイパーリンクの下線を表示する
TextDecoration オブジェクトを使用すると、テキストに視覚的な装飾を追加することができます。ただし、このオブジェクトのインスタンス化はパフォーマンスに影響を与える場合があります。 Hyperlink 要素を広く使用する場合は、MouseEnter イベントのようなイベントが発生したときにだけ下線を表示することを検討してください。 詳細については、「方法 : ハイパーリンク付き文字装飾を使用する」を参照してください。
MouseEnter で表示されるハイパーリンク
下線付きおよび下線なしで定義されている Hyperlink のマークアップのサンプルを次に示します。
<!-- Hyperlink with default underline. -->
<Hyperlink NavigateUri="https://www.msn.com">
MSN Home
</Hyperlink>
<Run Text=" | " />
<!-- Hyperlink with no underline. -->
<Hyperlink Name="myHyperlink" TextDecorations="None"
MouseEnter="OnMouseEnter"
MouseLeave="OnMouseLeave"
NavigateUri="https://www.msn.com">
My MSN
</Hyperlink>
次の表は、1,000 の Hyperlink 要素を下線付きで表示した場合と下線なしで表示した場合のパフォーマンスの比較を示しています。
Hyperlink |
作成時間 (ミリ秒) |
レンダリング時間 (ミリ秒) |
---|---|---|
下線付き |
289 |
1130 |
下線なし |
299 |
776 |
テキスト書式設定機能
WPF には、自動ハイフネーションなど、リッチ テキスト書式設定サービスが用意されています。 これらのサービスはアプリケーションのパフォーマンスに影響する可能性があるため、必要な場合にのみ使用するようにしてください。
不要なハイフネーションを使用しない
自動ハイフネーションを使用すると、テキストの行のハイフンで区切る必要がある箇所が検索されます。TextBlock オブジェクトや FlowDocument オブジェクトで、ハイフンで区切る位置を追加することもできます。 既定では、これらのオブジェクトでは自動ハイフネーション機能は無効になっています。 この機能を有効にするには、オブジェクトの IsHyphenationEnabled プロパティを true に設定します。 ただし、この機能を有効にすると、WPF でComponent Object Model (COM) 相互運用が開始されて、アプリケーションのパフォーマンスに影響する可能性があります。 自動ハイフネーションは、必要な場合以外は使用しないことをお勧めします。
Figure の使用に注意する
Figure 要素は、コンテンツ ページ内の絶対位置に配置できるフロー コンテンツの一部を表します。 Figure の位置が既に配置されているコンテンツと競合していると、場合によっては、ページ全体の書式が自動的に再設定されます。 Figure 要素を、隣どうしにまとめたり、固定ページ サイズのシナリオでコンテンツの上の方で宣言したりすることによって、不要な書式再設定が行われる可能性を最小限に抑えることができます。
最適な段落
FlowDocument オブジェクトの最適な段落機能は、スペースができるだけ均等に配分されるように段落を配置します。 既定では、最適な段落機能は無効です。 この機能を有効にするには、オブジェクトの IsOptimalParagraphEnabled プロパティを true に設定します。 ただし、この機能を有効にするとアプリケーションのパフォーマンスに影響します。 最適な段落機能は、必要な場合以外は使用しないことをお勧めします。