C# WinRT中ViewModel层上的虚拟化

C# WinRT中ViewModel层上的虚拟化,c#,xaml,mvvm,winrt-xaml,ui-virtualization,C#,Xaml,Mvvm,Winrt Xaml,Ui Virtualization,WPF和WinRT(C#+XAML)都使用支持UI虚拟化的面板(如virtualzingstackpanel等)支持UI虚拟化。使用MVVM时,需要使用某种类型的ItemsControl(ListBox,GridView,等等…)来完成,该控件绑定到视图模型上的可枚举属性(通常是ObservableCollection)。items控件仅为可见的项创建UI。之所以称之为UI虚拟化,是因为只有UI是虚拟化的。只有未显示的项目视图不会被创建,并且延迟到用户实际滚动到该项目的时刻。列表中的视图模型对象

WPF和WinRT(C#+XAML)都使用支持UI虚拟化的面板(如
virtualzingstackpanel
等)支持UI虚拟化。使用MVVM时,需要使用某种类型的
ItemsControl
ListBox
GridView
,等等…)来完成,该控件绑定到视图模型上的可枚举属性(通常是ObservableCollection)。items控件仅为可见的项创建UI。之所以称之为UI虚拟化,是因为只有UI是虚拟化的。只有未显示的项目视图不会被创建,并且延迟到用户实际滚动到该项目的时刻。列表中的视图模型对象都是预先创建的。因此,如果我有一个100000人的列表要展示,那么无论用户何时将其滚动到视图中,
ObservableCollection
都必须包含100000个视图模型

在我们的应用程序中,我们希望实现它,以便视图模型层是虚拟化的一部分。我们希望items控件显示一个滚动条,该滚动条适合可能加载的项目总数(因此可观察集合应使items控件认为它已包含100000个项目,以便滚动条视图端口的大小正确),但是我们希望在新项目即将进入视图时通知可观察集合,以便它可以从服务器加载实际对象。我们希望能够在加载的项中显示某种进度指示器,然后在将该项加载到可观察的集合中后,立即将其替换为该项的实际数据模板

我们希望尽可能地维护MVVM准则,但性能和响应能力是一个优先事项。如果可能的话,我们也更喜欢可重用的解决方案


解决这个问题的最佳方法是什么?

事实上,WinRT ItemsControl已经能够通过两种方式处理数据虚拟化: 1) 在实现IList或IObservableVector的自定义类中或通过从ObservableCollection继承来实现ISumportIncrementalLoading。这种方法非常简单,但它只支持线性滚动(不能跳过数据从第一个元素立即滚动到第1000000个元素),而且每次加载新的项目页面时,滚动条都会自动调整大小


2) 自己实现IObservableVector,第一次访问项时,只需返回null并启动加载过程。加载后,可以引发VectorChanged事件,指示该项不再为null。这是相当复杂的实现(很难依赖现有的ObservableVector实现),但它支持非线性滚动,甚至可以在控件长时间未访问项目时添加逻辑以卸载项目(从而节省内存并仅在需要时重新加载).

根据西蒙·费尔克斯的指导原则,我最终做了一个POC。我在这里添加代码以供将来参考

        public class VirtualizaingVector<T> : ObservableObject, IObservableVector<object>
        {

        public event VectorChangedEventHandler<object> VectorChanged;

        private Dictionary<int, T> _items;

        private int _count;
        private bool _countCalculated;

        private IItemSupplier<T> _itemSuplier;

        public VirtualizaingVector(IItemSupplier<T> itemSupplier)
        {
            _itemSuplier = itemSupplier;
            _items = new Dictionary<int, T>();
        }

        #region Notifications

        private void _notifyVectorChanged(VectorChangedEventArgs args)
        {
            if (VectorChanged != null)
            {
                VectorChanged(this, args);
            }
        }

        private void _notifyReset()
        {
            var args = new VectorChangedEventArgs(CollectionChange.Reset, 0);
            _notifyVectorChanged(args);
        }

        private void _notifyReplace(int index)
        {
            var args = new VectorChangedEventArgs(CollectionChange.ItemChanged, (uint)index);
            _notifyVectorChanged(args);
        }

        #endregion

        #region Private

        private void _calculateCount()
        {
            _itemSuplier.GetCount().ContinueWith(task =>
            {
                lock (this)
                {
                    _count = task.Result;
                    _countCalculated = true;
                }

                NotifyPropertyChanged(() => this.Count);
                _notifyReset();
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

        private void _startRefreshItemAsync(T item)
        {
            var t = new Task(() =>
            {
                _itemSuplier.RefreshItem(item);
            });

            t.Start(TaskScheduler.FromCurrentSynchronizationContext());
        }

        private void _startCreateItemAsync(int index)
        {
            var t = new Task<T>(() =>
            {
                return _itemSuplier.CreateItem(index);
            });

            t.ContinueWith(task =>
            {
                lock (this)
                {
                    _items[index] = task.Result;
                }
                _notifyReplace(index);
            }, TaskScheduler.FromCurrentSynchronizationContext());

            t.Start(TaskScheduler.FromCurrentSynchronizationContext());
        }


        #endregion

        public object this[int index]
        {
            get
            {
                T item = default(T);
                bool hasItem;

                lock (this)
                {
                    hasItem = _items.ContainsKey(index);
                    if (hasItem) item = _items[index];
                }

                if (hasItem)
                {
                    _startRefreshItemAsync(item);
                }
                else
                {
                    _startCreateItemAsync(index);
                }

                return item;
            }
            set
            {
            }
        }

        public int Count
        {
            get
            {
                var res = 0;
                lock (this)
                {
                    if (_countCalculated)
                    {
                        return res = _count;
                    }
                    else
                    {
                        _calculateCount();
                    }
                }

                return res;
            }
        }

    #region Implemenetation of other IObservableVector<object> interface - not relevant
    ...
    #endregion
}
    public interface IItemSupplier<T>
    {
        Task<int> GetCount();

        T CreateItem(int index);

        void RefreshItem(T item);
    }
公共类虚拟化向量:ObservableObject,IObservableVector
{
公共事件向量更改事件管理器向量更改;
私人词典(项目),;
私人国际单位计数;
计算私人布尔(bool);;
私人项目供应商(项目供应商);;
公共虚拟化向量(IItemSupplier itemSupplier)
{
_itemSupplier=itemSupplier;
_items=新字典();
}
#区域通知
私有void\u notifyVectorChanged(vectorchangedventargs args)
{
if(矢量已更改!=null)
{
矢量已更改(此为args);
}
}
私有void _notifyReset()
{
var args=new vectorchangedventargs(CollectionChange.Reset,0);
_notifyVectorChanged(args);
}
私有void\u notifyReplace(int索引)
{
var args=新向量changedventargs(CollectionChange.ItemChanged,(uint)索引);
_notifyVectorChanged(args);
}
#端区
#地区私人
私有void_calculateCount()
{
_itemSuplier.GetCount().ContinueWith(任务=>
{
锁(这个)
{
_计数=任务。结果;
_countCalculated=真;
}
NotifyPropertyChanged(()=>this.Count);
_notifyReset();
},TaskScheduler.FromCurrentSynchronizationContext());
}
私有void\u startRefreshItemAsync(T项)
{
var t=新任务(()=>
{
_ItemSupplier.RefreshItem(项目);
});
t、 启动(TaskScheduler.FromCurrentSynchronizationContext());
}
私有void\u startCreateItemAsync(int索引)
{
var t=新任务(()=>
{
返回_itemSuplier.CreateItem(索引);
});
t、 继续(任务=>
{
锁(这个)
{
_项目[索引]=任务结果;
}
_替换(索引);
},TaskScheduler.FromCurrentSynchronizationContext());
t、 启动(TaskScheduler.FromCurrentSynchronizationContext());
}
#端区
公共对象此[int索引]
{
得到
{
T项=默认值(T);
布尔哈斯项目;
锁(这个)
{
hasItem=\u items.ContainsKey(索引);
如果(hasItem)项=_项[索引];
}
如果(项目)
{
_startRefreshItemAsync(项目);
}
其他的
{
_startCreateItemAsync(索引);
}
退货项目;