none
如何使用”支持增量加载的动态数据集合类“实现增量加载? RRS feed

  • 问题

  • 我参照了网上的一些示例写了一个支持增量加载的动态数据集合类

    IncrementalLoadingCollection<T>

    但不会用它啊,应用程序一运行就开始狂加载(一次加载完所有数据!!!),也不是 ListView 滚动条到底部才加载!!!

    我用的是 MVVM 模式,希望给出一个正确的使用示例!

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using Windows.Foundation;
    using Windows.UI.Xaml.Data;
    
    namespace MyProject.Common
    {
        /// <summary>
        /// 提供 <see cref="ObservableCollection{T}"/> 扩展方法的类。
        /// </summary>
        public static class ObservableCollectionExtension
        {
            /// <summary>
            /// 将整个 <see cref="ObservableCollection{T}"/> 添加到目标 <see cref="ObservableCollection{T}"/> 中。
            /// </summary>
            /// <typeparam name="T">集合中的元素类型。</typeparam>
            /// <param name="source"><see cref="ObservableCollection{T}"/> 源。</param>
            /// <param name="target">目标 <see cref="ObservableCollection{T}"/>。</param>
            public static void AddTo<T>(this ObservableCollection<T> source, ObservableCollection<T> target)
            {
                foreach (var item in source)
                {
                    target.Add(item);
                }
            }
    
            /// <summary>
            /// 将指定集合的元素插入到 <see cref="ObservableCollection{T}"/> 的指定索引处。
            /// </summary>
            /// <typeparam name="T">集合中的元素类型。</typeparam>
            /// <param name="source"><see cref="ObservableCollection{T}"/> 源。</param>
            /// <param name="collection">应将其元素添加到 <see cref="ObservableCollection{T}"/> 的指定索引处的集合。</param>
            public static void InsertRange<T>(this ObservableCollection<T> source, int index, IEnumerable<T> collection)
            {
                foreach (var item in collection)
                {
                    source.Insert(index++, item);
                }
            }
        }
    
    
        /// <summary>
        /// 表示一个支持增量加载的动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。
        /// </summary>
        /// <typeparam name="T">集合中的元素类型。</typeparam>
        public class IncrementalLoadingCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
        {
            #region 内部类
    
            /// <summary>
            /// 表示加载更多项的方法。
            /// </summary>
            /// <param name="c">传播有关应取消操作的通知。</param>
            /// <param name="count">应加载更多项的数量。</param>
            /// <returns>返回一个已加载更多项的 <see cref="ObservableCollection{T}>"/>。</returns>
            public delegate Task<ObservableCollection<T>> LoadMoreItemsFuncAsync(CancellationToken c, uint count);
    
            #endregion
    
            #region 字段
    
            /// <summary>
            /// 表示加载更多项的方法。
            /// </summary>
            private LoadMoreItemsFuncAsync _loadMoreItemsFuncAsync;
            /// <summary>
            /// 支持增量加载实现的 Sentinel 值。
            /// </summary>
            private Func<bool> _hasMoreItems;
    
            #region 状态
    
            private bool _busy;
            private CancellationToken _cancellationToken;
    
            #endregion
    
            #endregion
    
            #region 属性
    
            /// <summary>
            /// 获取或设置该内部数据结构在不调整大小的情况下能够容纳的元素总数。
            /// </summary>
            public uint Capacity { get; set; }
            /// <summary>
            /// 获取 <see cref="IncrementalLoadingCollection{T}"/> 是否无元素数限制。
            /// </summary>
            public bool IsInfinite { get { return this.Capacity == 0; } }
    
            #endregion
    
            #region 构造函数
    
            /// <summary>
            /// 创建一个无元素数限制的 <see cref="IncrementalLoadingCollection{T}"/> 实例,并使用指定参数初始化。
            /// </summary>
            /// <param name="loadMoreItemsFuncAsync">表示加载更多项的方法。</param>
            /// <param name="hasMoreItems">表示定义一组是否还有更多项可以加载的方法。</param>
            public IncrementalLoadingCollection(LoadMoreItemsFuncAsync loadMoreItemsFuncAsync, Func<bool> hasMoreItems)
                : this(loadMoreItemsFuncAsync, hasMoreItems, 0)
            { }
    
            /// <summary>
            /// 创建一个 <see cref="IncrementalLoadingCollection{T}"/> 实例,并使用指定参数初始化。
            /// </summary>
            /// <param name="loadMoreItemsFuncAsync">表示加载更多项的方法。</param>
            /// <param name="hasMoreItems">表示定义一组是否还有更多项可以加载的方法。</param>
            /// <param name="capacity">设置 <see cref="IncrementalLoadingCollection{T}"/> 能够容纳的元素总数。</param>
            public IncrementalLoadingCollection(LoadMoreItemsFuncAsync loadMoreItemsFuncAsync, Func<bool> hasMoreItems, uint capacity)
            {
                this._loadMoreItemsFuncAsync = loadMoreItemsFuncAsync;
                this._hasMoreItems = hasMoreItems;
    
                Capacity = capacity;
    
            }
    
            #endregion
    
            #region 实现 ISupportIncrementalLoading
    
            /// <summary>
            /// 获取支持增量加载实现的 Sentinel 值。
            /// </summary>
            public bool HasMoreItems
            {
                get
                {
                    if (_cancellationToken.IsCancellationRequested)
                    {
                        return false;
                    }
    
                    if (this.IsInfinite)
                    {
                        return true;
                    }
    
                    if (this.Count <= Capacity)
                    {
                        return this._hasMoreItems();
                    }
    
                    return false;
                }
            }
    
            /// <summary>
            /// 初始化从视图的增量加载。
            /// </summary>
            /// <param name="count">要加载的项的数目。</param>
            /// <returns>加载操作的换行结果。</returns>
            public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
            {
                if (_busy)
                {
                    throw new InvalidOperationException("Only one operation in flight at a time.");
                }
    
                _busy = true;
    
                return AsyncInfo.Run((c) => InternalLoadMoreItemsAsync(c, count));
            }
    
            #endregion
    
            #region 私有方法
    
            /// <summary>
            /// 初始化从视图的增量加载的内部实现。
            /// </summary>
            /// <param name="c">传播有关应取消操作的通知。</param>
            /// <param name="count">要加载的项的数目。</param>
            /// <returns>加载操作的换行结果。</returns>
            private async Task<LoadMoreItemsResult> InternalLoadMoreItemsAsync(CancellationToken c, uint count)
            {
                try
                {
                    _cancellationToken = c;
    
                    var baseIndex = this.Count;
                    uint numberOfItemsTogenerate = 0;
    
                    if (!this.IsInfinite)
                    {
                        if (baseIndex + count < Capacity)
                        {
                            numberOfItemsTogenerate = count;
    
                        }
                        else
                        {
                            // 如果有元素数限制,并且需要加载更多项的数量超出了最大元素数,则只加载剩余元素数的更多项。
                            numberOfItemsTogenerate = Capacity - (uint)(baseIndex);
                        }
    
                    }
                    else
                    {
                        numberOfItemsTogenerate = count;
                    }
    
                    // 加载更多项的开始事件。
                    if (this.LoadMoreItemsStarted != null)
                    {
                        this.LoadMoreItemsStarted(numberOfItemsTogenerate);
                    }
    
                    // 开始加载更多项。
                    var intermediate = await this._loadMoreItemsFuncAsync(c, numberOfItemsTogenerate);
    
                    if (intermediate.Count == 0)  // 2016年2月16日11:01:56:友情提示,加粗部分可能导致增量加载失效,建议删除!
                    {
                        Capacity = (uint)this.Count;
                    }
                    else
                    {
                        // 将已加载的更多项添加到当前集合中。
                        intermediate.AddTo(this);
    
                        // 加载更多项的完成事件。
                        if (this.LoadMoreItemsCompleted != null)
                        {
                            this.LoadMoreItemsCompleted((uint)intermediate.Count);
                        }
                    }
    
                    return new LoadMoreItemsResult { Count = (uint)intermediate.Count };
                }
                finally
                {
                    this._busy = false;
                }
            }
    
            #endregion
    
            #region 委托与事件
    
            /// <summary>
            /// <see cref="LoadMoreItemsStarted"/> 的事件处理函数。
            /// </summary>
            /// <param name="count">实际加载的项的数目。</param>
            public delegate void LoadMoreItemsStartedEventHandler(uint count);
            /// <summary>
            /// <see cref="LoadMoreItemsCompleted"/> 的事件处理函数。
            /// </summary>
            /// <param name="count">实际加载的项的数目。</param>
            public delegate void LoadMoreItemsCompletedEventHandler(uint count);
    
            /// <summary>
            /// 当 <see cref="LoadMoreItemsFuncAsync"/> 开始时发生。
            /// </summary>
            public event LoadMoreItemsStartedEventHandler LoadMoreItemsStarted;
            /// <summary>
            /// 当 <see cref="LoadMoreItemsFuncAsync"/> 完成时发生。
            /// </summary>
            public event LoadMoreItemsCompletedEventHandler LoadMoreItemsCompleted;
    
            #endregion
        }
    }




    • 已编辑 CodingNinja10 2016年2月16日 3:03 修复内嵌代码bug
    2016年1月18日 7:54

答案

全部回复

  • 您好 CodingNinja10,

    当我们使用数据动态加载的时候,需要设置控件的一些Virtualizing属性。

    <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
    <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
    <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="True"/>

    以下文档包含一个完整的示例,供你参考。


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    2016年1月19日 7:43
    版主
  • Hi, CodingNinja10.

    我尝试添加你的代码到项目里,我在MainPage的后置代码里添加了如下代码:

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        IncrementalLoadingCollection<string> list = new IncrementalLoadingCollection<string>(LoadMoreItemAsync, HasMoreItemAsync);
        ls.ItemsSource = list;
    }
    
    private async Task<ObservableCollection<string>> LoadMoreItemAsync(CancellationToken c, uint count)
    {
        await Task.Delay(100);
    
        ObservableCollection<string> collection = new ObservableCollection<string>();
        for (int i = 0; i < 10; i++)
        {
            index++;
            collection.Add("Index:" + index);
        }
        return collection;
    }
    
    private bool HasMoreItemAsync()
    {
        if(index<int.MaxValue)
        {
            return true;
        }
        return false;
    }
    确实是正常的增量加载。不知道是否能贴出更多的代码来确定问题所在?

    2016年1月19日 8:58
  • 我找到原因了,没问题的,被 “下拉刷新” 影响了,具体详情请看 UWP的一种下拉刷新实现-cnblogs 评论 #27 及后面。
    2016年1月19日 13:27