以前、受信ストリームに基づいて追加される大量の行を表示する必要があるため、dataGridView のパフォーマンスについて質問しました。複数のソリューションが提供され、そのうちの 1 つは仮想モードを有効にしました。MSDN にはこのテーマに関する記事がありますが、データベースと編集可能なフィールドを使用しているため、必要以上に複雑に感じます。私の DataGridView は表示専用で、表示するデータはリストに配置されます。
回答を受け入れた後、次のリンクを受け取りました: http://www.codeproject.com/Articles/23937/Paging-Data-with-DataGridView-in-VirtualMode。これはデータベースの例を使用していますが、私が必要としているものにより適しています。表示したいデータを含む My List は、次のように宣言されます。
List<ResultRow> captureResults = new List<ResultRow>();
ResultRow オブジェクトは次のように定義されます。
/* Simplified */
public class ResultRow
{
private int first = 0;
private string second = "";
private UInt64 third = 0;
private IPAddress fourth = null;
/* etc */
public ResultRow()
{
}
public void Set (<the values>) //In actuallity a KeyValuePair
{
//field gets set here
}
public UInt64 Third
{
get { return third; }
set { third = value; }
}
/* etc. */
}
上記の記事に従って、ResultRowCache を作成しました。オブジェクトは次のように作成されます。
/* Page size set to 100. */
ResultRowCache _cache = new ResultRowCache(PAGE_SIZE, captureResults);
フォームの Load イベントで次のことを行います (この問題に関連しています。イベント ハンドラも追加しましたが、これは IDE を使用して行われたため、このコードには直接表示されません。以下の定義!)):
dataGrid.VirtualMode = true;
_cache = new ResultRowCache(PAGE_SIZE, captureResults);
dataGrid.Columns.Add("FirstColumn" , "First column header");
dataGrid.Columns.Add("Second Column", "Second column header");
/* Etc. Adding all columns. (Every member or ResultRow has it's own column. */
dataGrid.RowCount = (int)_cache.TotalCount;
私が疑問に思っていることの 1 つは、ここで RowCount がどのように初期化されるかです。おそらく 0 です (ResultRowCache のコンストラクター呼び出しのため (以下を参照)) が、再び変更されることはないようです。この割り当ては参照としてカウントされますか? それはどのように自分自身を更新しますか?
とにかく、私が持っているものを進めると、ResultRowCache は次のように定義されます。
public class ResultRowCache
{
public int PageSize = 100;
public long TotalCount;
public List<ResultRow> CachedData = null;
private List<ResultRow> FullData;
int _lastRowIndex = -1;
public ResultRowCache (int pageSize, List<ResultRow> total)
{
PageSize = pageSize;
FullData = total;
LoadPage( 0 );
}
public void LoadPage (int rowIndex)
{
int lastRowIndex = rowIndex - ( rowIndex % PageSize );
/* Page already loaded */
if( lastRowIndex == _lastRowIndex ) return;
/* New page */
_lastRowIndex = lastRowIndex;
/* Create a new cashes data object */
if( CachedData == null ) CachedData = new List<ResultRow>();
/* If cached data already existed, clear */
CachedData.Clear();
/* The index is valid (there is data */
if (lastRowIndex < FullData.Count)
{
/* Not a full page */
if (lastRowIndex + PageSize > FullData.Count)
{
CachedData = FullData.GetRange(lastRowIndex, ((lastRowIndex + PageSize) - 1) - FullData.Count);
}
/* Full page */
else
{
CachedData = FullData.GetRange(lastRowIndex, PageSize);
}
}
TotalCount = CachedData.Count;
}
}
}
最後に、データグリッドの CellValueNeeded イベントは次のように定義されています。
void DataGridCellValueNeededEvent(object sender, DataGridViewCellValueEventArgs e)
{
_cache.LoadPage(e.RowIndex);
int rowIndex = e.RowIndex % _cache.PageSize;
switch (dataGrid.Columns[e.ColumnIndex].Name)
{
/* Not actual names, example */
case "FirstColumn": e.Value = _cache.CachedData[rowIndex].First; break;
case "SecondColumn": e.Value = _cache.CachedData[rowIndex].Second; break;
/* Rest of the possibly columns/ResultRow values */
}
}
問題: 「captureResults」リストがいっぱいになっても、データグリッドは空のままです。これが私がこれまでに試したことです:
- イベントの切り替え後にデータグリッドの RowCount メンバーを更新します。
- キャッシュ内の結果の合計量でリストを宣言して、常に最新であることを確認します。(「外部の変更」は、参照であっても、キャッシュされたコンストラクターで渡したリストに渡されないのではないかと心配していました。(C#ではかなり新しい))
- フォームのロード イベントで、データグリッドの RowCount を 100 (ハード値) に設定します。
- captureResults リストに何かを追加した後、データグリッドに「Update()」呼び出しを追加しました。(これは、Invoke がリストに何かを追加する関数である特別なスレッドから発生します)
上記のどれも何も変更しませんでした。グリッドは空のままです。ここでかなり明白な何かが欠けていると思います。助言がありますか?
-編集-私がそれを機能させるために試みたものを追加しました。