簡単なスニファー機能を持ち、ユーザーがダンプしたパケットを表示するアプリケーションを作成しています。パケットはダンプ ファイルから読み取られ、現在のトラフィックでリアルタイムに更新され、キャプチャされたパケットが Datagrid に追加されます (各パケットは新しい行です)。データ バインディングを使用して、ICollectionChanged インターフェイスを実装するダンプ リーダーからパケットを取得するため、新しいパケットごとに Datagrid に通知されます。Datagrid は、仮想化と遅延スクロールを使用します。ほとんどすべてが正常に動作します - 唯一の例外は巨大な CPU 消費です。この CPU 使用率は、すべての新しいパケット (毎秒数千) を読み取り、それらを表示用にフォーマットし、Datagrid を更新するすべてのパケット上昇 CollectionChanged イベントが原因で発生します。ソフトウェアの要件は、ユーザーがすべての新しいパケットをリアルタイムで見る必要がないことです。誰もそれらすべてに気付かないように。ユーザーは一部のパケットしか表示できず、必要に応じて適切な位置までバーを上下にスクロールして、必要なパケットを表示できます。その場合にのみ、パケットをファイルから読み取る必要があります。
問題は、できればスクロールバーを実際のパケット数にスケーリングすることによって、新しいパケットが到着したことを示すために Datagrid を毎秒更新することですが、継続的に読み取り、フォーマットして CollectionChanged を呼び出す必要はありません。これには CPU 時間がかかり、さらにバーをスクロールしないと、新しいパケットは表示されません。到着するパケットの数が通知されるので、すべてのパケットの数がわかります。
Datagrid を実際のパケット数に合わせてスケーリングするためだけに、偽のパケットを追加しようとしました (それにより、すべての新しいパケットの読み取りとフォーマットを回避しました)。バーをスクロールするとCPU使用率が低下し、パケットが読み取り専用になるため、ほとんど機能しました。しかし、しばらくすると、新しいパケットが Datagrid に追加されると、現在のビューの行がランダムに重複し始めました。それらは 1 番目、2 番目、3 番目、そして 1 番目、2 番目、3 番目と表示されました。
私もバインディングを更新しようとしました - それはうまくいきましたが、最初のアプローチよりも多くのリソースを消費したので、それを放棄しました. また、Datagrid の listcollectionview を更新しようとしました - バインディングの更新と同じ効果です。
次のことは、CollectionChanged への 1 回の呼び出しでパケットのリストを追加したかったときです。
NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list));
範囲アクションがサポートされていないというエラーが発生しました。PresentationFramework.dll!System.Windows.Data.ListCollectionView.ValidateCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) によってスローされます。
そのため、パケットのリスト内のすべてのパケットに対して CollectionChanged を呼び出す必要があり、これにより CPU 使用率も増加しました。
要約すると、新しいパケット/行があることを Datagrid に通知したいので、Datagrid のスクロール バーはパケットの総数に合わせてスケーリングされますが、パケットはユーザーがバーをスクロールしたときにのみファイルから読み取られます。
私の問題を解決するためのあらゆる提案に非常に感謝しています。
言及すべき重要なことが 1 つあります。申し訳ありませんが、最初にそれについて話すのを忘れていました。私の ObservableCollection は、ある種のデータ仮想化をサポートしており、メモリには必要なパケットのみを保持します。新しいパケットが到着したら、コレクションに新しいパケットの数を通知するだけで、コレクションの Add メソッドを呼び出す必要はありません。実際にはパケットを追加しないため、実際に読み取られるパケットはありません. コレクションには、その瞬間のパケットの総数に関する情報と、実際にメモリに保持されているパケットの量が少ないため、グリッドで表示できます。パケットは必要な場合にのみオンザフライで読み取られ、表示されていない場合は解放されます。しかし、新しいパケットについてグリッドに通知するには、CollectionChanged を呼び出す必要があります (別のより良い方法はわかりません)。この呼び出しの引数で、すべての新しいパケットを提供する必要があります。そして、CollectionChanged へのこの呼び出しにより、パケットが実際に読み取られ、CPU が消費されます。スクロールバーのサイズが新しいパケットが到着したことを通知するようにグリッドを再スケーリングしたいが、パケットを読み取り、新しいパケットごとに CollectionChanged を呼び出す必要がない (これは毎秒何千回も発生する可能性がある). スクロール バーの位置が変更されたときにのみパケットを読み取るようにしたいので、各パケットを処理するコストのかかる操作は、すべてのパケットではなく、現在表示されている少数のパケットに対してのみ実行されます。