1

我正在尝试在我的 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。

4

1 回答 1

0

知道该错误是总是被抛出还是仅在您开始滚动时才被抛出会很有趣。然后绑定可能会要求您提供尚不可用的索引,因为您尚未在 VirtualCollection 中意识到它。

尽管老实说,我感觉您在索引器中使用了错误的绑定路径语法。

看看这些链接:

http://msdn.microsoft.com/en-us/library/ms742451.aspx

http://msdn.microsoft.com/en-us/library/system.windows.data.binding.path.aspx

例如:

<Binding Path="[key]" .../>

key 必须是字典或哈希表的类型化索引,或者数组的整数索引。此外,键的值必须是可直接绑定到应用它的属性的类型。例如,可以使用包含字符串键和字符串值的哈希表来绑定到 TextBox 的 Text。

意味着如果您的索引器是整数类型,您将在 XAML 中需要类似的内容。

<Binding Path="[(sys:Int32)42,(sys:Int32)24]"... />

我不确定您为什么要手动创建绑定。你可以在 xaml 中做到这一点,对吗?:)

于 2013-10-28T11:38:20.590 回答