【问题标题】:Load more items with ListView (Windows 8.1)使用 ListView 加载更多项目 (Windows 8.1)
【发布时间】:2014-03-03 13:44:13
【问题描述】:

我正在使用 ListView 在 XAML-C# 中开发一个 Windows 应用商店应用程序。

每次用户到达列表末尾时,我都想在 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{

公共类 GeneratorIncrementalLoadingClass: 增量加载基础

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

    }
}

【问题讨论】:

  • 我们需要的一些东西:目前正在发生什么?应该发生什么?是否正在加载项目?物品没有变化吗? LoadMoreItemsOverrideAsync 是否被调用过?
  • LoadMoreItemsOverrideAsync 被调用。最后,所有项目都加载完毕。我认为while循环有问题。问题是每次执行 while 循环时列表都会滚动到顶部:如果用户在执行 while 循环时尝试向列表末尾滚动,则滚动位置将重置到列表顶部。跨度>

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


【解决方案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 是否有任何剩余物品。

您之前所做的是在开始时延迟加载所有项,而不是增量加载(根据用户当前正在执行的操作分块。

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

无论如何,希望对您有所帮助并祝您编码愉快!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-13
    • 2015-12-03
    相关资源
    最近更新 更多