質問
MVVMを使用してwpfアプリを構築しています。以下のような遅延読み込みを使用するviewModelがあります:
public class AssignmentsViewModel
{
List<AssignmentViewModel> _Assignments;
public List<AssignmentViewModel> Assignments
{
get
{
if (_Assignments == null)
_Assignments = new List<AssignmentViewModel>(Data.GetAssignments());
return _Assignments;
}
set { _Assignments = value; }
}
}
public class AssignmentViewModel
{
List<NoteViewModel> _Notes;
public List<NoteViewModel> Notes
{
get
{
if (_Notes == null)
_Notes = new List<NoteViewModel>(Data.GetNotes());
return _Notes;
}
set { _Notes = value; }
}
}
ビューには、ListViewにAssignmentViewModelsがあり、datatemplateを使用してListBoxにメモがあります。
160個のアイテムを持つAssignmentViewModelsの1つを表示すると、ロードに1000ミリ秒かかります。これは、Notesプロパティが150万行のデータベーステーブルから取得したためだと思いました。私はそれをチェックしましたが、Notesリストを作成するのに60msしかかかりませんでした。ですから、160個のアイテムをリストボックスにロードするデータバインディングだと思います。しかし、リストボックスはコンテンツを仮想化するので、そうではありません(私はそれをスヌープし、アイテムが仮想化スタックパネルにあることを確認しました)。
だから私は途方に暮れていて、何が余分な940ミリ秒かかっているのかを知る方法がわかりません。
これを追跡するにはどうすればよいですか?パフォーマンスが重要であり、それを改善する方法がわかりません。
解決
UIがどのようなものかを知らなくても、BitmapEffectsの大量使用のように、UIから何かが遅くなる可能性があります。
また、のように、リストをObservableCollectionに切り替えることもできます。これにバインドすると、リストよりも大幅に高速になります。
いくつかの優れたパフォーマンスプロファイリングツールもありますあなた。
他のヒント
XAMLを確認できますか?
スタックパネル(または同様のコントロール)内にリストボックスがある場合、リストボックスはそのエントリを仮想化しません。
これにより仮想化されます:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row=0>Your label here</Label>
<ListBox Grid.Row=1 Your properties here... />
</Grid>
これはしません:
<StackPanel>
<Label>Your label here</Label>
<ListBox Your properties here... />
</StackPanel>
スタックパネル内では、リストボックスは最大の高さまでレンダリングされ、スクロールはスタックパネルに依存します。 (仮想化なし)
グリッド内では、リストボックスはグリッド行の高さにレンダリングされ、独自のスクロールビューアを使用します。 (仮想化)
グリッド内に配置する代わりに、リストボックスのHeightプロパティまたはMaxHeightプロパティを設定することもできますが、stackpanelを使用する場合は、何らかの自動サイズ調整が必要だと思います。
スタックパネルをグリッドの内側に配置してもチートはできないことに注意してください。リストボックスはどこかから最大の高さを引き出すことができなければなりません。
遅延ロードなしでパフォーマンステストを試みましたか?
また、アイテムのDataTemplates / Viewsはどのくらい複雑ですか?単純なTextBlock表現を使用したテストを実行して、ビューがそれほど複雑でないことを確認してください。
SQLをもう一度見て、コレクションがいっぱいであることを確認します。 L2Sは、結果を列挙するまで実際には入力されません。
private IEnumerable<Thing> Method1()
{
return _db.Things.Where(t => t.ID > 500);
}
private void Method2()
{
var things = Method1(); // here, 'things' doesn't really contain anything
// even though you've already done the db fetch
foreach (var thing in things) // 'things' gets populated here.
{
// do something
}
}
ex:データが返された直後に .ToList()
を呼び出します。データ層では、ビジネス層またはUI層までは発生しなかった可能性のあるアクティビティがさらに多く表示されます。
わかりました。時々非常に大きいため、テキストの折り返しが必要なテキストブロックがありました。私は明示的にデータテンプレート内に幅を設定して(ラップするように)、何らかの理由でListBox内のVirtualizingStackPanelにリスト内のすべてのアイテムを認識させます。グリッドに収まるように作り直しました。
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
....
</Grid>
<ListBox Grid.Column="1" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
...//DataTemplate
</ListBox>
</Grid>
これにより、リストボックスがすばやく読み込まれました。彼らが私を正しい道に導いてくれたあなたの答えをありがとう。