使用ListView加载更多项目(Windows 8.1)

时间:2014-02-06 14:38:42

标签: c# xaml listview windows-store-apps

我正在使用ListView在XAML-C#Windows Store App中开发。

每次用户到达列表末尾时,我想在ListView上加载更多项目。

我刚刚阅读了此讨论Load more items on grid view scroll end,我刚刚阅读了此示例http://code.msdn.microsoft.com/windowsapps/Data-Binding-7b1d67b5

我尝试实现类似提到的示例: - 我的一些代码

                uint nentries = 0;

            // Follow method return a int: number of items (entries) between 0 and 20
            nentries = await App.EntriesViewModel.LoadEntries(0);
            if (nentries != 0) 
            {
//entries is a GeneratorIncrementalLoadingClass<Entry> 
                        App.EntriesViewModel.entries = new GeneratorIncrementalLoadingClass<Entry>(nentries, count =>
                        {
                            //EntriesOc is an observable collection with INotifyPropertyChanged
                            return App.EntriesViewModel.EntriesOc.ElementAt(count);
                        });
  //entriesCVS is a CollectionViewSource defined into xaml code
                        entriesCVS.Source = App.EntriesViewModel.entries; 
                    }

            }
            this.DataContext = null;
            this.DataContext = App.EntriesViewModel;

             //until here it's works

            if (nentries == 20)
            {
                uint n = 0;
                while (nentries % 20 == 0)
                {
                    n = await App.EntriesViewModel.LoadEntries(nentries);
                    if (n == 0) break; // no more data to load
                    nentries += n;
                    App.EntriesViewModel.entries = new GeneratorIncrementalLoadingClass<Entry>(nentries, (count) =>
                    {
                        return App.EntriesViewModel.EntriesOc.ElementAt(count);
                    });


                    // without the follow line of code the CollectionViewSource doesn't update
                    // however the list scroll to the top (I want to remove this behaviour)
                    entriesCVS.Source = App.EntriesViewModel.entries; 
                }
            }
  • IncrementalLoadingBase.cs(示例中的同一文件)

      

    命名空间MySolution {

    public abstract class IncrementalLoadingBase: IList, ISupportIncrementalLoading, INotifyCollectionChanged
    
    {
    
        public int Add(object value)
        {
            throw new NotImplementedException();
        }
    
        public void Clear()
        {
            throw new NotImplementedException();
        }
    
        public bool Contains(object value)
        {
            return _storage.Contains(value);
        }
    
        public int IndexOf(object value)
        {
            return _storage.IndexOf(value);
        }
    
        public void Insert(int index, object value)
        {
            throw new NotImplementedException();
        }
    
        public bool IsFixedSize
        {
            get { return false; }
        }
    
        public bool IsReadOnly
        {
            get { return true; }
        }
    
        public void Remove(object value)
        {
            throw new NotImplementedException();
        }
    
        public void RemoveAt(int index)
        {
            throw new NotImplementedException();
        }
    
        public object this[int index]
        {
            get
            {
                return _storage[index];
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    
        public void CopyTo(Array array, int index)
        {
            ((IList)_storage).CopyTo(array, index);
        }
    
        public int Count
        {
            get { return _storage.Count; }
        }
    
        public bool IsSynchronized
        {
            get { return false; }
        }
    
        public object SyncRoot
        {
            get { throw new NotImplementedException(); }
        }
    
        public IEnumerator GetEnumerator()
        {
            return _storage.GetEnumerator();
        }
    
    
        public bool HasMoreItems
        {
            get { return HasMoreItemsOverride(); }
        }
    
        public Windows.Foundation.IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
        {
            if (_busy)
            {
                throw new InvalidOperationException("Only one operation in flight at a time");
            }
    
            _busy = true;
    
            return AsyncInfo.Run((c) => LoadMoreItemsAsync(c, count));
        }
    
    
        public event NotifyCollectionChangedEventHandler CollectionChanged;
    
    
        async Task<LoadMoreItemsResult> LoadMoreItemsAsync(CancellationToken c, uint count)
        {
            try
            {
                var items = await LoadMoreItemsOverrideAsync(c, count);
                var baseIndex = _storage.Count;
    
                _storage.AddRange(items);
    
                // Now notify of the new items
                NotifyOfInsertedItems(baseIndex, items.Count);
    
                return new LoadMoreItemsResult { Count = (uint)items.Count };
            }
            finally
            {
                _busy = false;
            }
        }
    
        void NotifyOfInsertedItems(int baseIndex, int count)
        {
            if (CollectionChanged == null)
            {
                return;
            }
    
            for (int i = 0; i < count; i++)
            {
                var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _storage[i + baseIndex], i + baseIndex);
                CollectionChanged(this, args);
            }
        }
    
    
        protected abstract Task<IList<object>>LoadMoreItemsOverrideAsync(CancellationToken c, uint count);
        protected abstract bool HasMoreItemsOverride();
    
    
        List<object> _storage = new List<object>();
        bool _busy = false;
    
    }
    
         

    }

  • GeneratorIncrementalLoadingClass.cs(示例中的同一文件)

    namespace MySolution{
     

public class GeneratorIncrementalLoadingClass:   IncrementalLoadingBase

    {
        public GeneratorIncrementalLoadingClass(uint maxCount, Func<int, T> generator)
        {

            _generator = generator;
            _maxCount = maxCount;
        }

        protected async override Task<IList<object>> LoadMoreItemsOverrideAsync(System.Threading.CancellationToken c, uint count)
        {
            uint toGenerate = System.Math.Min(count, _maxCount - _count);

            // Wait for work 
            await Task.Delay(10);

            // This code simply generates
            var values = from j in Enumerable.Range((int)_count, (int)toGenerate)
                         select (object)_generator(j);
            _count += toGenerate;

            return values.ToArray();
        }

        protected override bool HasMoreItemsOverride()
        {
            return _count < _maxCount;
        }


        Func<int, T> _generator;
        uint _count = 0;
        uint _maxCount;

    }
}

1 个答案:

答案 0 :(得分:1)

LoadMoreItemsOverrideAsync打算做的是用更多项目填充当前列表。您目前正在做的是每次重置列表。这就是每次页面滚动到顶部的原因。它正在加载一组全新的数据。

这是我尝试修复它。

首先,如果你在示例中注意到,他们会解释MaxCount只是一个例子而不是必需的。你真正想要的是让你的清单知道它何时到达列表的末尾。这意味着应该检查nentries

您的新IncrementalLoading实现应该与此类似(如果不是这样)。

public class GeneratorIncrementalLoadingClass: IncrementalLoadingBase
{
    private int _numLeft;
    private Func<int, Task<int>> _loadMore;

    public GeneratorIncrementalLoadingClass(Func<int, Task<int>> loadMore, Func<int, T> generator)
    {
        _loadMore = loadMore;
        _generator = generator;
    }

    protected async override Task<IList<object>> LoadMoreItemsOverrideAsync(System.Threading.CancellationToken c, uint count)
    {
        // If count is greater than the max size that we know, load the difference first
        List<object> returnList = new List<object>();
        if(count > 20)
        {
            var tempList = await LoadMoreItemsOverrideAsync(c, count);
            returnList.AddRange(tempList);
        }
        // Find out if there are enough left that it's asking for
        uint toGenerate = System.Math.Min(count, _numLeft);

        // Wait for load
        _numLeft = await _loadMore(toGenerate);

        // This code simply generates
        var values = from j in Enumerable.Range((int)_count, (int)toGenerate)
                     select (object)_generator(j);
        _count += toGenerate;

        return values.ToList().AddRange(tempList);
    }

    protected override bool HasMoreItemsOverride()
    {
        return _numLeft > 0;
    }


    Func<int, T> _generator;
    uint _count = 0;
    uint _maxCount;

}

然后你就这样使用它。

// Move these outside of the loop
entriesCVS.Source = App.EntriesViewModel.entries; 
App.EntriesViewModel.entries = new GeneratorIncrementalLoadingClass<Entry>(App.EntriesViewModel.LoadEntries, (index) =>
{
    return App.EntriesViewModel.EntriesOc.ElementAt(index);
});

应该现在发生的是它在开始时设置CollectionViewSource,然后懒洋洋地将数据加载到基本集合(EntriesOc)中。当ListView滚动到底部时,它应自动调用LoadMoreItemsOverrideAsync。这将做的是调用您的异步加载函数并存储响应(可以加载的数字)。然后,它可以根据该响应通知ListView是否有任何项目。

之前您正在做的是在开始时懒洋洋地加载所有项目,而不是逐步加载(以块为单位,基于用户当前正在做的事情。

此解决方案仍然不完美。如果用户非常快速向下滚动并按顺序加载所有内容,它将会出现问题(我试图考虑过)。理想的情况是,如果您的后台加载函数可以接受优先加载的一系列项目,以便最后加载最后的项目而不必等待列表的其余部分加载。

无论如何,希望有帮助和快乐的编码!