WPF DataGrid制作整个ItemsSource的卷影副本

时间:2018-07-21 17:05:26

标签: c# wpf data-binding wpfdatagrid

我最近开始研究一个WPF应用程序,该应用程序具有一个包含大量数据的DataGrid。 ItemsSource的绑定方式如下:

ItemsSource={Binding MyData, Mode=OneWay}

MyData是实现以下接口的自定义集合:

public sealed class GridCollection 
    : IList<object[]>, INotifyCollectionChanged, INotifyPropertyChanged

基础数据永远不会更改,而只会在某些触发器上完全重新加载。该馆藏拥有大量数据。由于列可以在重新生成数据源后发生更改,因此它们会在每个ItemsChanged(在继承的类中)之后(如下所示(很可能与问题无关,但谁知道))进行重建:

using (Dispatcher.DisableProcessing())
{
    Columns.Clear();
    for (var i = 0; i < columns.Count; i++)
    {
        var bindingBase = new Binding("[" + i + "]") {Mode = BindingMode.OneTime};

        var column = new DataGridTextColumn
        {
            Header = columns[i].Name,
            Binding = bindingBase,
            IsReadOnly = true
        };
        Columns.Add(column);
    }
}

现在的问题是,DataGrid似乎在其自己的ObservableCollection中保存了集合中数据的完整副本,正如我在内存分析器中可以清楚地看到的那样(直接进入大对象堆) )。我只能假设此行为是为了避免多线程问题,或者它与GridView有关?可能是因为DataGrid无法将ItemsSource识别为List,而只能识别为IEnumerable?我还可以确认WPF制作了ItemsSource的副本之后,从未访问过集合中的数据。

我想要实现的是DataGrid直接在我的集合上运行,以提高内存效率和时间效率(因为创建此副本肯定需要时间)。

我已经玩过诸如CollectionSynchronization之类的东西,因为我认为也许如果我告诉我我负责同步,那么它就不会复制,

BindingOperations.EnableCollectionSynchronization(gridCollection, _collectionLock);

但它不会改变行为。我还确保行虚拟化可以在网格上正常工作。

有人知道为什么网格保留此副本以及如何避免这种情况吗?我的意思是肯定它可能必须保留一些数据(至少对于可见行),但是对于整个集合来说似乎有些过分……

更新 这些行没有重复,但是CollectionView创建了一个ObservableCollection,该数组具有引用我集合中所有条目的大型后备数组。 实现ICollectionView为我解决了该问题。现在,我得到了最奇怪的行为:

    public IEnumerator<object[]> GetEnumerator()
    {
        lock (_lock)
        {
            if (_backingField == null) // Breakpoint hits when first refreshing view, but not second time!
                yield break;

            // Breakpoint hits second time, _backingField is null --> Exception
            for (var i = 0; i < _backingField.Count; i++) yield return _backingField[i];
        }
    }

所以我第二次刷新视图时,不执行空检查。我将对_backingField的所有访问都放在一个锁中,_backingField是易失性的,并且禁用了编译优化。听起来像代码对我重新排序,但我什至在null检查和for之间放置了一个内存屏障,但这无济于事。这是WPF特定于ICollectionView的吗?我再次检查了断点是否始终在主线程上。有什么想法吗?

更新2 现在完全有道理,因为它是IEnumerable,所以在调试器中没有命中第二个检查。以某种方式实现ICollectionView会使DataGrid(或其他人)开始枚举,并在引发OnCollectionChanged重置事件后继续进行此操作。仍然不知道为什么,由于在resets事件之前背景字段已更改,因此枚举器此时无效。如果您知道最佳做法,请告诉我:)

0 个答案:

没有答案