我正在使用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;
}
}
答案 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
是否有任何项目。
之前您正在做的是在开始时懒洋洋地加载所有项目,而不是逐步加载(以块为单位,基于用户当前正在做的事情。
此解决方案仍然不完美。如果用户非常快速向下滚动并按顺序加载所有内容,它将会出现问题(我试图考虑过)。理想的情况是,如果您的后台加载函数可以接受优先加载的一系列项目,以便最后加载最后的项目而不必等待列表的其余部分加载。
无论如何,希望有帮助和快乐的编码!