パフォーマンスの最適化 : レイアウトとデザイン
更新 : 2007 年 11 月
WPF アプリケーションの設計によっては、レイアウトの計算やオブジェクト参照の検証で不要なオーバーヘッドが発生して、パフォーマンスに影響が及ぶことがあります。オブジェクトの作成 (特に起動時の作成) はアプリケーションのパフォーマンス特性に影響する可能性があります。
このトピックでは、このようなパフォーマンスに関する推奨事項について説明します。
レイアウト
"レイアウト パス" という用語は、Panel 派生オブジェクトの子のコレクションのメンバを測定および配置して、それらを画面上に描画するプロセスを表します。レイアウト パスは数学的に増大するプロセスで、コレクション内の子の数が多くなれば、必要な計算の数も多くなります。たとえば、コレクション内の子 UIElement オブジェクトがその位置を変更するたびに、レイアウト システムによる新しいパスがトリガされる可能性があります。オブジェクトの特性とレイアウトの動作の間には密接な関係があるため、レイアウト システムを呼び出すことができるイベントの種類を把握することが重要です。レイアウト パスの不要な呼び出しをできるだけ減らすことで、アプリケーションのパフォーマンスを向上させることができます。
レイアウト システムは、コレクションの子メンバごとに、測定パスと配置パスという 2 つのパスを実行します。各子オブジェクトは、それぞれ固有のレイアウト動作を提供するために、Measure メソッドと Arrange メソッドの独自のオーバーライドされた実装を提供します。簡単に言うと、レイアウトは、要素のサイズ測定、配置、および画面上への描画を繰り返す再帰的なシステムです。
子 UIElement オブジェクトは、最初にそのコア プロパティを測定して、レイアウト プロセスを開始します。
オブジェクトのサイズに関連する FrameworkElement プロパティ (Width、Height、Margin など) が評価されます。
DockPanel の Dock プロパティや StackPanel の Orientation プロパティなど、Panel 固有のロジックが適用されます。
すべての子オブジェクトが測定された後、コンテンツが配置されます。
子オブジェクトのコレクションが画面に描画されます。
以下のアクションが発生すると、再びレイアウト パス プロセスが呼び出されます。
子オブジェクトがコレクションに追加された場合。
子オブジェクトに LayoutTransform が適用された場合。
子オブジェクトに対して UpdateLayout メソッドが呼び出された場合。
測定パスや配置パスに影響を与えるものとしてメタデータでマークされている依存関係プロパティの値が変更された場合。
可能な場合は最も効率的なパネルを使用する
使用する Panel 派生要素のレイアウト動作はレイアウト プロセスの複雑さに直接影響します。たとえば、Grid コントロールや StackPanel コントロールには Canvas コントロールよりはるかに多くの機能が用意されていますが、その代償として、パフォーマンスへの負荷も高くなります。Grid コントロールに用意されている機能が必要ない場合は、Canvas やカスタム パネルなど、パフォーマンスへの負荷が低いコントロールを代わりに使用するようにしてください。
詳細については、「パネルの概要」を参照してください。
RenderTransform は置き換えずに更新する
RenderTransform プロパティの値として、Transform を置き換えずに更新できる場合があります。アニメーションを含むシナリオでは特にこれが当てはまります。既存の Transform を更新すると、不要なレイアウト計算が開始されるのを防ぐことができます。
ツリーはトップダウンで作成する
論理ツリーのノードが追加または削除されると、ノードの親とそのすべての子でプロパティの無効化が行われます。このため、常にトップダウンの作成パターンに従って、検証済みのノードで無駄に無効化が行われないようにする必要があります。ツリーをトップダウンで作成した場合とボトムアップで作成した場合の実行速度の違いを次の表に示します。このツリーには 150 のレベルがあり、各レベルに TextBlock と DockPanel が 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;
}
}
論理ツリーの詳細については、「WPF のツリー」を参照してください。