パフォーマンスの最適化: レイアウトとデザイン

WPF アプリケーションの設計によっては、レイアウトの計算やオブジェクト参照の検証で不要なオーバーヘッドが発生して、パフォーマンスに影響が及ぶことがあります。 オブジェクトの作成 (特に実行時の作成) はアプリケーションのパフォーマンス特性に影響する可能性があります。

このトピックでは、このようなパフォーマンスに関する推奨事項について説明します。

レイアウト

"レイアウト パス" という用語は、Panel 派生オブジェクトの子のコレクションのメンバーを測定および配置して、それらを画面上に描画するプロセスを表します。 レイアウト パスは数学的に増大するプロセスで、コレクション内の子の数が多くなれば、必要な計算の数も多くなります。 たとえば、コレクション内の子 UIElement オブジェクトがその位置を変更するたびに、レイアウト システムによる新しいパスがトリガーされる可能性があります。 オブジェクトの特性とレイアウトの動作の間には密接な関係があるため、レイアウト システムを呼び出すことができるイベントの種類を把握することが重要です。 レイアウト パスの不要な呼び出しをできるだけ減らすことで、アプリケーションのパフォーマンスを向上させることができます。

レイアウト システムは、コレクションの子メンバーごとに、測定パスと配置パスという 2 つのパスを実行します。 それぞれの子オブジェクトでは、独自のレイアウト動作を提供するために、Measure および Arrange メソッドの独自のオーバーライドされた実装が提供されます。 簡単に言うと、レイアウトは、要素のサイズ測定、配置、画面上への描画を繰り返す再帰的なシステムです。

  • UIElement オブジェクトでは、最初にそのコア プロパティが測定され、レイアウト プロセスが開始されます。

  • WidthHeightMargin など、サイズに関連するオブジェクトの FrameworkElement のプロパティが評価されます。

  • Panel 固有のロジックが適用されます。たとえば、DockPanelDock プロパティや StackPanelOrientation プロパティなどです。

  • すべての子オブジェクトが測定された後、コンテンツが配置されます。

  • 子オブジェクトのコレクションが画面に描画されます。

以下のアクションが発生すると、再びレイアウト パス プロセスが呼び出されます。

  • 子オブジェクトがコレクションに追加された場合。

  • 子オブジェクトに LayoutTransform が適用されます。

  • 子オブジェクトに対して UpdateLayout メソッドが呼び出されます。

  • 測定パスや配置パスに影響を与えるものとしてメタデータでマークされている依存関係プロパティの値が変更された場合。

可能な場合は最も効率的なパネルを使用する

使用する Panel 派生要素のレイアウト動作は、レイアウト プロセスの複雑さに直接基づいています。 たとえば、Grid または StackPanel コントロールでは Canvas コントロールよりもはるかに多くの機能が提供されます。 はるかに多くの機能が用意されていますが、その代償として、パフォーマンスへの負荷も高くなります。 しかし、Grid コントロールに用意されている機能が必要ない場合は、Canvas やカスタム パネルなど、コストのかからない代替手段を使用してください。

詳細については、「Panels Overview」を参照してください。

RenderTransform は置き換えずに更新する

TransformRenderTransform プロパティの値として置き換えずに、更新することができます。 アニメーションを含むシナリオでは特にこれが当てはまります。 既存の Transform を更新すると、不要なレイアウト計算が開始されないようにすることができます。

ツリーはトップダウンで作成する

論理ツリーのノードが追加または削除されると、ノードの親とそのすべての子でプロパティの無効化が行われます。 このため、常にトップダウンの作成パターンに従って、検証済みのノードで無駄に無効化が行われないようにする必要があります。 次の表に、ツリーをトップダウンで作成した場合とボトムアップで作成した場合の実行速度の違いを示します。このツリーには 150 のレベルがあり、各レベルに TextBlockDockPanel が 1 つずつ含まれています。

動作 ツリーの作成 (ミリ秒) レンダリング-ツリーの作成を含む (ミリ秒)
ボトムアップ 366 454
トップダウン 11 96

ツリーをトップダウンで作成する方法を次のコード例に示します。

private void OnBuildTreeTopDown(object sender, RoutedEventArgs e)
{
    TextBlock textBlock = new TextBlock();
    textBlock.Text = "Default";

    DockPanel parentPanel = new DockPanel();
    DockPanel childPanel;

    myCanvas.Children.Add(parentPanel);
    myCanvas.Children.Add(textBlock);

    for (int i = 0; i < 150; i++)
    {
        textBlock = new TextBlock();
        textBlock.Text = "Default";
        parentPanel.Children.Add(textBlock);

        childPanel = new DockPanel();
        parentPanel.Children.Add(childPanel);
        parentPanel = childPanel;
    }
}
Private Sub OnBuildTreeTopDown(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim textBlock As New TextBlock()
    textBlock.Text = "Default"

    Dim parentPanel As New DockPanel()
    Dim childPanel As DockPanel

    myCanvas.Children.Add(parentPanel)
    myCanvas.Children.Add(textBlock)

    For i As Integer = 0 To 149
        textBlock = New TextBlock()
        textBlock.Text = "Default"
        parentPanel.Children.Add(textBlock)

        childPanel = New DockPanel()
        parentPanel.Children.Add(childPanel)
        parentPanel = childPanel
    Next i
End Sub

論理ツリーについて詳しくは、「WPF のツリー」をご覧ください。

関連項目