DataGrid で水平仮想化を実行しようとしています。私のコレクションのタイプは次のとおりです。
List<string[]>
最初の次元の長さは 64 で、2 番目の次元の長さはおよそ 5000 です
垂直仮想化を実現するために、 Paul McClean の VirtualCollectionを使用しています。
私の IItemsProvider は、テーブル内の行を表す string[] のアイテムを返す Iterator をカプセル化します。
マイ アイテム プロバイダー:
public class ArrayItemProvider<I,T> :IArrayItemProvider, IItemsProvider<T[]> where I : IList<T>
{
public int FetchCount()
{
return 64;
}
public IList<T[]> FetchRange(int startIndex, int count)
{
return _iterator.Skip(startIndex).Take(count).ToList();
}
}
イテレータ:
public class ArrayItemIterator<I, T> : IArrayItemIterator<T> where I : IList<T>
{
public IEnumerator<T[]> GetEnumerator()
{
for (int i = 0; i < _arrayItemLength; i++)
{
T[] arr = new T[_extent];
for (int j = 0; j < _extent; j++)
{
arr[j] = _items[j][i];
}
yield return arr;
}
}
public int Extent
{
get { return _extent; }
}
public void UpdateExtent(int extent)
{
_extent = extent;
}
}
}
上記を要約すると、 VirtualCollection を介して特定の範囲の string[] のアイテムを受け取ります。
私が今試みているのは、列も仮想化することです。私の列は実行時に特定の Extent によって生成されます。これは、静的クラス DataGridBuilderUtil の添付プロパティのコールバックで行われます
cs:
for (int i = 0; i < _iterator.Extent; i++)
{
_dataGrid.Columns.Add(CreateColumn(i));
}
private static DataGridColumn CreateColumn(int i)
{
var column = new DataGridTextColumn();
column.Header = "View - " + (i + 1);
column.Binding = new Binding("[" + i + "]");
return column;
}
DataGridBuilderUtil では、水平範囲が変更されたときに、DataGrid の ScrollViewer ScrollChanged イベントも添付します。
1) 新しい列を追加します。
2) 別の列に対応するために Iterators Extent を更新します。
3) 同じ位置に垂直に再スクロールします。これにより、IList から派生した ItemsSource (VirtualCollection) が作成され、インデックスのクエリが実行され、現在のページが再度要求されます (フラグ IsDefferedLoadPageRequired の助けを借りて)。
private static void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if(e.HorizontalChange > 0.0)
{
// add column (1)
_dataGrid.Columns.Add(CreateColumn(_dataGrid.Columns.Count));
// Update the Extent (2)
_iterator.UpdateExtent(_dataGrid.Columns.Count);
// Makes the VirtualCollection request the current page again. (3)
_collection.IsDefferedLoadPageRequired = true;
_scrollViewer.ScrollToVerticalOffset(_scrollViewer.VerticalOffset);
}
}
だから今、VirtualCollectionの中に
public T this[int index]
{
get
{
....
int pageIndex = index / PageSize;
RequestPage(pageIndex);
....
}
}
ItemsProvider を要求します:
public IList<T[]> FetchRange(int startIndex, int count)
{
return _iterator.Skip(startIndex).Take(count).ToList();
}
これは Iterator を要求します。別の列に対応するために Extent がインクリメントされたことを思い出してください。
public IEnumerator<T[]> GetEnumerator()
{
for (int i = 0; i < _arrayItemLength; i++)
{
T[] arr = new T[_extent];
for (int j = 0; j < _extent; j++)
{
arr[j] = _items[j][i];
}
yield return arr;
}
}
これで、文字列 [20] アイテムがインクリメントされた文字列 [] アイテムが文字列 [21] になりました。私の水平方向のデータ仮想化は機能しました。
問題は、この方法でバインドされている私のセルです:(上記の CreateColumn メソッドから)
column.Binding = new Binding("[" + i + "]");
新しい列の各セルにバインディング エラーがあります(コレクションの読み込み時に生成された元の列のバインディングは正常に機能します:
System.Windows.Data Error: 17 : Cannot get 'Item[]' value (type 'String') from '' (type 'String[]'). BindingExpression:Path=[20]; DataItem='String[]' (HashCode=32127640);
target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String') ArgumentOutOfRangeException:'System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: index'
これは、列が作成されたときにその行の配列項目にそのインデックスが含まれていないという事実に関係していると思います。代わりに、配列が更新された後に列を作成しようとしました。
それは同じ結果でした。
ここでの大きな問題は、なぜバインディングが機能しないのか、バインディングを更新するにはどうすればよいのかということです。
さらに、DataGrid.EnableColumnVirtualization = True を設定して、これをすべてまとめます (バインディングが機能する場合)。
編集 :
コレクションが更新された後に列を作成しようとしました:
_collection.LoadCompletedEvent += OnLoadCompleted; // VirualCollection event after page is loaded.
private static void OnLoadCompleted(object sender, EventArgs e)
{
_dataGrid.Columns.Add(CreateColumn(_dataGrid.Columns.Count));
}
private static void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if(e.HorizontalChange > 0.0)
{
// Update the Extent
_iterator.UpdateExtent(_dataGrid.Columns.Count+1);
// Makes the VirtualCollection request the current page again.
_collection.IsLoadPageRequired = true;
_scrollViewer.ScrollToVerticalOffset(_scrollViewer.VerticalOffset);
}
}
OnLoadComplete は、VirtualCollection が現在のページを再読み込みした後に発生します。