简体   繁体   English

WPF DataGrid列虚拟化

[英]WPF DataGrid Column Virtualization

I'm attempting to perform an horizontal virtualization on my DataGrid . 我正在尝试在DataGrid上执行水平虚拟化。 My collection is of type : 我的收藏类型是:

   List<string[]>

which it's first dimension length is 64 and second is roughly 5000 它的第一维长度是64,第二维长度是5000

I've been using Paul McClean's VirtualCollection to achieve vertical virtualization 我一直在使用Paul McClean的VirtualCollection实现垂直虚拟化

My IItemsProvider encapsulates an Iterator which returns item's of string[] which represents a row in my table. 我的IItemsProvider封装了一个迭代器,该迭代器返回string []的项目,该项目代表表中的一行。

My ItemsProvider : 我的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();
    }
}  

The Iterator : 迭代器:

 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;
    }
}

} }

To summarize the above i receive Items of string[] for a specific range through VirtualCollection . 综上所述,我通过VirtualCollection接收了特定范围内的string []项目。

What i am attempting now is to Virtualaize the Columns as well, My Columns are generated at run time by a given Extent , this is done in an attached property's callback , in a static class DataGridBuilderUtil 我现在正在尝试对列进行虚拟化,“我的列”是在运行时由给定的范围生成的,这是在附加属性的回调中,在静态类DataGridBuilderUtil中完成的

cs : 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;
   }

in DataGridBuilderUtil i also attach the DataGrid's ScrollViewer ScrollChanged event , When an Horizontal Extent is changed : 在DataGridBuilderUtil中,当水平范围更改时,我还附加了DataGrid的ScrollViewer ScrollChanged事件:

1) I add a new Column. 1)我添加一个新的列。

2) I update the Iterators Extent to accommodate another column . 2)我更新了迭代器范围以容纳另一列。

3) I re-scroll to the same position Vertically , this makes my ItemsSource (VirtualCollection) which derives from IList to query it's index and request the current page again( with the help of my flag IsDefferedLoadPageRequired ) 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);
       }
  }

So now inside the VirtualCollection 所以现在在VirtualCollection中

    public T this[int index]
    {
        get
        {
            ....
            int pageIndex = index / PageSize;
            RequestPage(pageIndex);                
            ....
        }             
     }

which quires the ItemsProvider : 要求ItemsProvider:

    public IList<T[]> FetchRange(int startIndex, int count)
    {
        return _iterator.Skip(startIndex).Take(count).ToList();
    }

which quires the Iterator , Remember that our Extent was incremented to accommodate another column. 它需要Iterator,请记住,我们的范围已增加以容纳另一列。

    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;
        }
    }

So now i have a string[] item's which was incremented string[20] items are now string[21] My Horizontal Data Virtualization worked. 因此,现在我有一个string []项目,该项目已递增string [20]项目现在为string [21]我的水平数据虚拟化工作了。

The Problem is that my cells which are bound in this fashion : (From CreateColumn method above) 问题是我的单元格以这种方式绑定:(来自上面的CreateColumn方法)

 column.Binding = new Binding("[" + i + "]");

have a binding error , in each of the cells in a new column (The binding's in the original columns which are generated when loading the collection work fine : 在新列的每个单元格都有一个绑定错误(在加载集合时生成的原始列中的绑定工作正常:

      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'

I think this has to do with the fact that when my column is created the array item for that row does not contain that index , alternatively i also tried creating the column after the array was updated. 我认为这与以下事实有关:创建我的列时,该行的数组项不包含该索引,或者我也尝试在更新数组后创建该列。

that had the same result. 结果相同。

The big question here is , Why doesn't The Binding work , and how to refresh the binding ? 这里最大的问题是:绑定为什么不起作用,以及如何刷新绑定?

Additionally i set DataGrid.EnableColumnVirtualization = True to bring this all together (if the binding would of worked). 另外,我设置了DataGrid.EnableColumnVirtualization = True可以将所有这些组合在一起(如果绑定可以工作的话)。

Edit : 编辑:

Iv'e also attempted to create the column after the collection was updated : Iv'e还尝试在更新集合后创建该列:

     _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 is raised after the VirtualCollection ReLoads the current page. VirtualCollection重新加载当前页面后,将引发OnLoadComplete。

It would be interesting to know if that error is always being thrown or only when you start scrolling. 知道该错误是始终抛出还是仅在您开始滚动时才是很有趣的。 Then the binding might be asking for index that is not available yet since you havent realized it yet in your VirtualCollection. 然后,绑定可能会询问尚不可用的索引,因为您尚未在VirtualCollection中意识到它。

Though to be honest I have a feeling you are using binding path syntax wrong with the indexers. 坦白地说,我有一种感觉,您在索引器中使用的绑定路径语法错误。

Take a look at those links: 看一下这些链接:

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

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

As example: 例如:

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

key must be either the typed index to a dictionary or hash table, or the integer index of an array. 键必须是字典或哈希表的类型化索引,或者是数组的整数索引。 Also, the value of the key must be a type that is directly bindable to the property where it is applied. 同样,键的值必须是可直接绑定到应用该键的属性的类型。 For instance, a hash table that contains string keys and string values can be used this way to bind to Text for a TextBox. 例如,包含字符串键和字符串值的哈希表可以通过这种方式绑定到TextBox的Text。

Means if your indexer is of type integer you will need something like this in your XAML. 意味着如果索引器的类型为整数,则XAML中将需要类似这样的内容。

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

I am not sure why you creating your binding manually. 我不确定为什么要手动创建绑定。 You could have done that in xaml, coudnt you? 您可以在xaml中完成此操作,对吗? :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM