6

アプリケーションに、表示するデータのような巨大なテーブルを含むビューがあります。データは、ネストされた2つのUniformGridに表示されます。UniformGridsはItemsControlsのItemPanelであり、いくつかのViewModelにバインドされています。次の画像といくつかのXAMLコードの例を参照してください。

ビューとビューモデルhttp://img593.imageshack.us/img593/8825/stackoverflowuniformgri.png

<!-- The green boxes -->
<ItemsControl ItemsSource="{Binding BigCells}">

  <ItemsControl.ItemPanel>
    <PanelTemplate>
      <UniformGrid />
    </PanelTemplate>
  </ItemsControl.ItemPanel>

  <ItemsControl.ItemTemplate>
    <DataTemplate>

      <!-- The blue boxes -->
      <ItemsControl ItemsSource="{Binding SmallCells}">
        <ItemsControl.ItemPanel>
          <PanelTemplate>
            <UniformGrid />
          </PanelTemplate>
        </ItemsControl.ItemPanel>
      </ItemsControl>

    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

ここで、ビューのサイズを変更できるようにしたいのですが、すべての小さなボックスのレイアウトが計算されるため、これはまったくうまく機能しません。
これは、すべてのボックスで同じであるため、少なくともボックスのサイズは1回だけ実行できます。

WPFで大量のコントロールを表示するためのベストプラクティスは何ですか/どこから最適化を開始できますか?キーワードは、WPFでのUniformGridsのパフォーマンスの最適化を引き続き発見するのに役立ちます。

4

2 に答える 2

8

DataGrid同時に大量のWPFを使用すると、同様の問題が発生しました。それは最終的に2つの主要な問題に帰着しました:

  • リソース辞書
  • バインディング

リソース辞書

注意せずに基本的なWPF機能を使用すると、これらが大きなボトルネックになる可能性があります。

ItemsControl私の場合、私のを含む単一の論理スクロールの場合、 ResourceDictionaryへの呼び出しとResourceDictionaryでDataGridsの1000万の呼び出しのようなものを取得していました。処理に1秒以上かかります。GetValueGetValueWithoutLock

この主要なResourceDictionaryアクセス数は、さまざまなソースによって引き起こされました。

  • 動的なリソースの取得:ControlTemplates、より一般的にはいくつかのリソースディクショナリにリソースが配置されていて、を介してそれらにアクセスする場合は、それらを{DynamicResource ...}削除します。どこかに静的なものを用意し、それらに直接アクセスします。
  • スタイルのフェッチ:使用する別のビジュアルにスタイルがない場合、またはスタイルはあるがFrameworkElement.OverridesDefaultStyleプロパティを設定していない場合、WPFはすべてのリソースでコントロールに一致するスタイルを見つけようとします。リソース辞書への多くのアクセス。これを回避するには、必ずコントロールのすべてのコントロールテンプレートをオーバーライドし、すべてのコントロールに設定Style={x:Null}してください(コントロールのスタイルが必要な場合は、コントロールにインラインで設定して、追加してくださいOverridesDefaultStyle = true
  • 暗黙のデータテンプレート:暗黙のデータテンプレートは非常に便利ですが、無料にはほど遠いです。適用するを探すときDataTemplate、WPFはもう一度リソースディクショナリを参照して、DataTemplate一致するViewModelsタイプを見つけます。解決策はDataTemplate、ViewModelにバインドされたすべてのコントロールにセレクターを使用し、正しいを取得するための最適化された方法を実装することですDataTemplate。(私は個人的に、resourceDictionaryから1回だけ取得するdataTemplateの静的フィールドと、DataTemplateSelector必要に応じてそれらを返す最適化されたフィールドを持っています。)

バインディング

バインディングは非常に便利ですが、メモリとパフォーマンスの面で非常に高価です。非常に賢明な環境では、パフォーマンスの観点から、注意せずにそれらを使用することはできません。たとえば、バインディングでは、バインドされたオブジェクトごとに最大3つのWeakReferenceを作成したり、リフレクションを使用したりできます。最終的に、DataContextのPropertyChangedイベントでバインディングとプラグインされたイベントハンドラーのほとんどすべてを削除することになりました。DataContextコントロール(イベントがある)で変更があった場合、サポートがあるDataContextChangedかどうかをテストし、DataContextサポートしINotifyPropertyChangedている場合は、イベントにイベントハンドラーをアタッチし、PropertyChanged必要に応じてプロパティを更新します。(私は一方向のバインディングしか持っていなかったので、これが私が物事を行うこの方法を選んだ理由ですが、他の解決策があります)。

MVVMとWPFを使用しているときにバインディングを使用しないのはイライラするように思えるかもしれませんが、私の場合、バインディングを最適化する方法は実際にはありませんでした。

最後に、オブジェクト(たとえば、ブラシなど)がある場合は、可能であればそれらにFreezable躊躇しないでください。Freeze

これらは、コードを最適化するのに役立つアドバイスです。実際に起こっていることを監視し、結果に応じて適応させるには、プロファイラー(これは私が今まで使用した中で最高のプロファイラーであるため、常にdotTraceを使用することをお勧めします)が必要になります。

幸運を !

于 2012-12-20T10:50:24.130 に答える
4

すべてのボックスが同じサイズであることが確実な場合は、いつでも独自のUniformGridクラスを作成し、オーバーライドMeasureOverrideして1つの子のみを測定できます。現在のプロジェクトでも同じサイズの内部コントロールを備えた大きなUniformGridがいくつかある可能性があるので、あなたのアイデアに興味をそそられ、すぐに試してみることにしました。

私はPetzoldのUniformGridAlmostクラスをインスピレーションとして使用しましたが、UniformGrid代わりにサブクラス化されPanelているため、行と列の依存関係プロパティを複製する必要はありません。UniformGrid計算された行と列の数をその派生クラスに公開していないように見えるため、SilverlightのUniformGridクラスから実装を借用し、メソッドを使用しましたUpdateComputedValues

私のクラスではこのMeasureOverrideように見えます

  protected override Size MeasureOverride(Size sizeAvailable)
  {
     if (InternalChildren.Count == 0)
     {
        return new Size(0, 0);
     }

     UpdateComputedValues();

     // Calculate a child size based on uniform rows and columns.
     Size sizeChild = new Size(sizeAvailable.Width / ComputedColumns,
                               sizeAvailable.Height / ComputedRows);

     // Assume children will measure to at least a comparable size.
     UIElement child = InternalChildren[0];
     child.Measure(sizeChild);

     double width = Math.Max(width, child.DesiredSize.Width);
     double height = Math.Max(height, child.DesiredSize.Height);
     return new Size(ComputedColumns * width, ComputedRows * height);
  }

したがって、これがキッカーです。10000TextBlocksを使用してItemsControlでテストしたところ、UniformGridのバージョンは高速でしたが、ごくわずかでした(1%未満-MeasureOverrideの呼び出しが約95%高速であることを確認しました)。ですから、これはおそらく役に立たないでしょうが、私は自分の経験を共有したいと思いました。あなたの場合、均一なレイアウトを決定するためにすべてのボックスを測定する必要はないというあなたの観察は真実ですが、その測定はそれを妨げているものではありません。これらのコントロールはすべて描画する必要があるため、とにかく後で測定されると確信しています。ResourceDictionary / Bindingの提案から、より多くのことを得ることができるでしょう。

于 2012-12-20T18:12:11.777 に答える