我的Windows Phone 8.1应用程序中有一个ListView
,我可以获得1000或更多结果,所以每次滚动到底时我需要实现加载更多功能,或者其他一些逻辑和自然触发向列表添加更多项目的方式
我发现ListView
支持ISupportIncrementalLoading
,并发现了这种实现:https://marcominerva.wordpress.com/2013/05/22/implementing-the-isupportincrementalloading-interface-in-a-window-store-app/
这是我找到的更好的解决方案,因为它没有指定类型,即它是通用的。
我对此解决方案的问题是,当加载ListView时,LoadMoreItemsAsync
会一直运行所需的所有时间,直到获得所有结果,这意味着用户不会触发加载更多。我不确定是什么使LoadMoreItemsAsync
触发器,但是有些事情是不对的,因为它假定当我打开页面并在现场加载所有项目时,没有我做任何事情或任何滚动。这是实施:
IncrementalLoadingCollection.cs
public interface IIncrementalSource<T> {
Task<IEnumerable<T>> GetPagedItems(int pageIndex, int pageSize);
void SetType(int type);
}
public class IncrementalLoadingCollection<T, I> : ObservableCollection<I>, ISupportIncrementalLoading where T : IIncrementalSource<I>, new() {
private T source;
private int itemsPerPage;
private bool hasMoreItems;
private int currentPage;
public IncrementalLoadingCollection(int type, int itemsPerPage = 10) {
this.source = new T();
this.source.SetType(type);
this.itemsPerPage = itemsPerPage;
this.hasMoreItems = true;
}
public bool HasMoreItems {
get { return hasMoreItems; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count) {
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(
async () => {
uint resultCount = 0;
var result = await source.GetPagedItems(currentPage++, itemsPerPage);
if(result == null || result.Count() == 0) {
hasMoreItems = false;
}
else {
resultCount = (uint)result.Count();
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() => {
foreach(I item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
这是PersonModelSource.cs
public class DatabaseNotificationModelSource : IIncrementalSource<DatabaseNotificationModel> {
private ObservableCollection<DatabaseNotificationModel> notifications;
private int _type = "";
public DatabaseNotificationModelSource() {
//
}
public void SetType(int type) {
_type = type;
}
public async Task<IEnumerable<DatabaseNotificationModel>> GetPagedItems(int pageIndex, int pageSize) {
if(notifications == null) {
notifications = new ObservableCollection<DatabaseNotificationModel>();
notifications = await DatabaseService.GetNotifications(_type);
}
return await Task.Run<IEnumerable<DatabaseNotificationModel>>(() => {
var result = (from p in notifications select p).Skip(pageIndex * pageSize).Take(pageSize);
return result;
});
}
}
我稍微改了一下,因为对我的数据库的调用是异步的,这是我发现确保在填充集合之前可以等待查询的唯一方法。
在我的DatabaseNotificationViewModel.cs
IncrementalNotificationsList = new IncrementalLoadingCollection<DatabaseNotificationModelSource, DatabaseNotificationModel>(type);
一切都很好,除了不那么正常的“加载更多”。我的代码有什么问题?
答案 0 :(得分:9)
我创建了一个非常简单的此问题示例here,并在MSDN论坛here上提出了此问题。老实说,我不知道为什么会发生这种奇怪的行为。
我观察到的内容
LoadMoreItemsAsync
,计数为1.我假设这是为了确定单个项目的大小,以便它可以计算出请求下次调用的项目数。LoadMoreItemsAsync
应该在第一次调用后立即发生,但使用正确的项目数(count> 1),然后不再调用{{1除非你向下滚动,否则会发生。但是,在您的示例中,它可能会错误地再次调用LoadMoreItemsAsync
并重新计算。LoadMoreItemsAsync
计数为1,直到LoadMoreItemsAsync
变为false ,在这种情况下,它一次加载一个项目。发生这种情况时,ListView加载项目时会出现明显的UI延迟。但是, UI线程未被阻止。 ListView只是通过顺序调用HasMoreItems
来占用UI线程。LoadMoreItemsAsync
的多次调用,如果先前的调用未加载所有项目,则单次调用LoadMoreItemsAsync(1)
。导致问题的原因
LoadMoreItemsAsync(> 1)
方法中的问题似乎是非常短暂等待的任务(等待您将项目添加到列表后的任务清单很好。)LoadMoreItemsAsync
内的所有await
,则不会发生此问题,从而强制它同步执行。具体来说,如果删除LoadMoreItemsAsync
包装器和dispatcher.RunAsync
(只是模拟项目),那么ListView将表现得很好。await source.GetPagedItems
后,即使您添加的所有内容都是看似无害的await
,问题也会重新出现。多奇怪啊!如何解决问题
如果在await Task.Run(() => {})
通话中花费的大部分时间都在等待下一页项目的HTTP请求,正如我预期的大多数应用程序都是,那么问题就不会发生。所以,我们可以通过等待LoadMoreItemsAsync
来延长方法花费的时间,如下所示:
Task.Delay(10)
我只为你的例子提供了(hacky)解决方法,但没有解释原因。 如果有人知道为什么会这样,请告诉我。
答案 1 :(得分:3)
这不是导致此问题的唯一因素。如果您的ListView在ScrollViewer中,它将继续加载所有项目,并且不会正确虚拟化,从而对性能产生负面影响。解决方案是为ListView提供特定的高度。