Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 创建一个列表视图,其中LoadMoreItemsAsync位于滚动的末尾_C#_Xaml_Listview_Windows Store Apps_Windows Phone 8.1 - Fatal编程技术网

C# 创建一个列表视图,其中LoadMoreItemsAsync位于滚动的末尾

C# 创建一个列表视图,其中LoadMoreItemsAsync位于滚动的末尾,c#,xaml,listview,windows-store-apps,windows-phone-8.1,C#,Xaml,Listview,Windows Store Apps,Windows Phone 8.1,我的Windows Phone 8.1应用程序中有一个ListView,我可以得到1000个或更多的结果,因此每次滚动触底时,我都需要实现加载更多功能,或者使用其他逻辑和自然方式触发向列表中添加更多项目。 我发现ListView支持ISupportIncrementalLoading,并发现以下实现: 这是我找到的更好的解决方案,因为它没有指定类型,即它是泛型的。 这个解决方案的问题是,当加载ListView时,LoadMoreItemsAsync会运行所有需要的时间,直到获得所有结果,这意味着

我的Windows Phone 8.1应用程序中有一个
ListView
,我可以得到1000个或更多的结果,因此每次滚动触底时,我都需要实现加载更多功能,或者使用其他逻辑和自然方式触发向列表中添加更多项目。
我发现
ListView
支持
ISupportIncrementalLoading
,并发现以下实现: 这是我找到的更好的解决方案,因为它没有指定类型,即它是泛型的。

这个解决方案的问题是,当加载ListView时,
LoadMoreItemsAsync
会运行所有需要的时间,直到获得所有结果,这意味着用户不会触发LoadMore。我不确定是什么导致了
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>();
    }
}
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;
            });
    }
}
IncrementalNotificationsList = new IncrementalLoadingCollection<DatabaseNotificationModelSource, DatabaseNotificationModel>(type);
我对它做了一点修改,因为对我的数据库的调用是异步的,这是我找到的唯一一种确保在填充集合之前可以等待查询的方法。

在my
DatabaseNotificationViewModel.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>();
    }
}
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;
            });
    }
}
IncrementalNotificationsList = new IncrementalLoadingCollection<DatabaseNotificationModelSource, DatabaseNotificationModel>(type);
IncrementalNotificationsList=新的IncrementalLoadingCollection(类型);


除了不太正常的“加载更多”之外,一切正常。我的代码出了什么问题?

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

我观察到的

  • ListView将首先调用
    LoadMoreItemsAsync
    ,计数为1。我假设这是为了确定单个项目的大小,以便计算出下一次调用需要请求的项目数
  • 如果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
    方法中的问题似乎是等待时间很短的任务(将项目添加到列表后等待任务是可以的)
  • 如果删除
    LoadMoreItemsAsync
    中的所有
    wait
    s,从而强制同步执行,则不会发生此问题。具体地说,如果删除
    dispatcher.RunAsync
    包装器和
    wait source.GetPagedItems
    (只需模拟这些项),则ListView将运行良好
  • 删除所有的
    wait
    s后,即使添加的都是看似无害的
    wait任务,问题也会再次出现。运行(()=>{})
    。真奇怪
如何解决问题

如果在
LoadMoreItemsAsync
调用中花费的大部分时间都在等待下一页项目的HTTP请求,正如我预期的那样,那么问题就不会发生。因此,我们可以通过等待
任务来延长在方法中花费的时间。延迟(10)
,如下所示:

wait Task.WhenAll(Task.Delay(10),dispatcher.RunAsync(corespatcherpriority.Normal,()=>
{
foreach(结果中的I项)
本条增加(项目);
}).AsTask());


我只是为您的示例提供了一个(黑客)解决方法,但没有解释原因如果有人知道发生这种情况的原因,请告诉我。

这不是唯一可能导致此问题的原因。如果您的ListView位于ScrollViewer中,它将继续加载所有项目,并且不会正确虚拟化,从而对性能产生负面影响。解决方案是为ListView指定一个特定的高度。

我复制了您的代码,无法复制错误。这对我来说是正确的。如果你想查看我在一个新的空白应用程序项目中使用的代码。你是否尝试添加300/400项,并检查我提到的行为?不管我有多少项,我都尝试了1000项,但当我向下滚动到列表末尾时,它仍然只加载更多项。代码中有一些多余的东西,但我认为这不会影响增量加载行为?你能提供一份完整的复印件吗?如果ListView的高度是无限的,那么它需要立即加载所有内容。有趣的是,我得到的解决方法与您的完全相同,即延迟。虽然不是完美的解决方案,但它是一个功能解决方案,从我的角度来看,您清楚地确定了