我在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运行所需的所有时间,直到获得所有结果为止,这意味着Load More不是由用户触发的。我不确定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);

除了不太正常的“加载更多”之外,其他所有功能都可以正常工作。我的代码有什么问题?

最佳答案

我创建了此问题here的非常简化的示例,并在MSDN论坛here上提出了此问题。老实说,我不知道为什么发生这种奇怪的行为。

我观察到的东西

  • ListView将首先以1计数调用LoadMoreItemsAsync。我认为这是为了确定单个项目的大小,以便它可以计算出要下一次调用的项目数。
  • 如果ListView表现良好,则应在第一次调用后立即进行第二次LoadMoreItemsAsync的调用,但项数正确(计数> 1),然后除非向下滚动,否则将不再发生对LoadMoreItemsAsync的调用。但是,在您的示例中,它可能会错误地再次调用计数为1的LoadMoreItemsAsync
  • 在最坏的情况下(实际上在您的示例中经常发生)是ListView将继续按顺序反复调用LoadMoreItemsAsync(计数为1),直到HasMoreItems变为false(在这种情况下,它已加载所有项目一次。发生这种情况时,ListView加载项目时会有明显的UI延迟。但是,UI线程未被阻止。 ListView只是通过顺序调用LoadMoreItemsAsync来占用UI线程。
  • 虽然ListView不会总是穷尽所有项目。有时它将加载100或200或500个项目。在每种情况下,模式都是:多次调用LoadMoreItemsAsync(1),然后如果不是先前调用未加载所有项,则一次调用LoadMoreItemsAsync(> 1)
  • 它似乎仅在页面加载时发生。
  • 该问题在Windows Phone 8.1和Windows 8.1上仍然存在。

  • 是什么引起问题
  • 在将项目添加到列表之前,问题似乎是,非常短暂,等待已久的任务(在将项目添加到列表后等待任务)。
  • 如果删除LoadMoreItemsAsync内的所有await,则不会发生此问题,从而强制其同步执行。具体来说,如果删除LoadMoreItemsAsync包装器和dispatcher.RunAsync(仅模拟项目),则ListView将表现良好。
  • 删除了所有await source.GetPagedItems之后,即使您添加的只是看似无害的await,该问题也会再次出现。真奇怪!

  • 如何解决问题

    如果像我期望的大多数应用程序那样,在await Task.Run(() => {})调用中花费的大部分时间都在等待下一页项目的HTTP请求,则不会发生此问题。因此,我们可以通过等待LoadMoreItemsAsync来延长在该方法上花费的时间,如下所示:

    await Task.WhenAll(Task.Delay(10), dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        foreach (I item in result)
            this.Add(item);
    }).AsTask());
    

    我只是为您的示例提供了一个(棘手的)解决方法,而没有提供原因的解释。 如果有人知道为什么会这样,请告诉我。

    关于c# - 在滚动结束时使用LoadMoreItemsAsync创建一个ListView,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26719617/

    10-10 08:32