我正在尝试在我的 DataGrid 上执行水平虚拟化。我的收藏是类型:
List<string[]>
它的第一个维度长度是 64,第二个维度大约是 5000
我一直在使用 Paul McClean 的 VirtualCollection来实现垂直虚拟化
我的 IItemsProvider 封装了一个迭代器,它返回字符串 [] 的项目,它代表我表中的一行。
我的 ItemsProvider :
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 接收特定范围的字符串 [] 项。
我现在正在尝试对列进行虚拟化,我的列是在运行时由给定的 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)我重新垂直滚动到相同的位置,这使得我的 ItemsSource (VirtualCollection) 派生自 IList 来查询它的索引并再次请求当前页面(在我的标志 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);
}
}
在 VirtualCollection 重新加载当前页面后引发 OnLoadComplete。