今天,老周有小故事讲,国庆期间。有一次老周在某站台上候公交车。老周旁边也站满了人,突然,有一位头发弄得像电线杆的小伙子,不知为何,没有先兆地就大笑起来。

老周先是看了那小伙子一眼,他手上没有拿什么东西(一瓶水),也没在看书或手机,就这样忽然大笑不止。随即,老周便后退了两步,观察一下其他人有什么反应。

一位年轻妈妈拉着小女儿走开了,接着,一位大叔走到垃圾桶旁边拨弄烟灰;然后,一位女孩后退了一两步;站在发笑者旁边的一位先生蹲下身来,不知道在做什么……

如果是你,你会有什么反应?

====================================================

好了,“恐怖”故事讲完了,下面进入正题。

本次老周就和大伙儿说说有关增量加载的实现方法,其实叫做“预提取”感觉也可行。当列表控件要绑定一个非常大的列表时,如果一次性加载所有数据,估计是很费时间的,特别是当数据是从网络上提取的情况下。

在这种情形下,是相当有必要考虑使用增量加载方案,我也不知道什么叫增量加载,就是按照Incremental Load直接翻译过来的,至于合适不合适,见仁见智吧。重要的是,我们知道它是个什么东东就行,管它叫什么。

支持增量加载的控件必须是ListViewBase类的派生类。ListViewBase类公开一个LoadMoreItemsAsync方法,调用它可以执行预提取操作。正常情况下,不需要我们去调用它,当列表控件的滚动条滚动到边沿时,会自动触发预提取操作。

但是,如果你把IncrementalLoadingTrigger属性设置为IncrementalLoadingTrigger.None的话,就需要手动触发增量加载了,一般来说没必要这么干,它控件自己完成就好了。

支持增量加载的集合必须实现ISupportIncrementalLoading接口,这个接口只有两个成员:

1、只读属性HasMoreItems,如果还有要增量加载的项,就返回true,如果没有项要加载了,就返回false。

2、LoadMoreItemsAsync方法。当增量加载操作被激活后这个方法会被调用,我们需要实现这个方法来提取数据。方法原型如下:

IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(System.UInt32 count);

由于该方法返回的是IAsyncOperation<TResult>,即专用于Windows运行时的异步操作,因为实现接口的类型没有直接公开(大概是以COM形式注册),不能在代码中直接实例化,所以要借助System.Runtime.InteropServices.WindowsRuntime命名空间下的AsyncInfo静态类来完成。把基于.net的异步方法(返回Task或Task<TResult>)转换为Windows运行时的异步操作方法。方法参数count表示此次加载操作要提取的数据条数。

我们还要考虑这么个问题:增量加载修改了源集合后,UI上显示的列表也应该同步更新,故集合除了要实现ISupportIncrementalLoading接口外,还应该实现INotifyCollectionChanged接口。

如果我们自定义集合类来实现这两个接口,也不是不可,不过有更简单的方法。ObservableCollection<T>类身就已经实现了INotifyCollectionChanged接口,所以,我们直接从ObservableCollection<T>类派生,同时实现ISupportIncrementalLoading接口,这样一来,可以省去不少功夫,何乐而不为?

知道了原理,按照老周的惯用手段,就应该上示例了。

咱们先写一个万能抽象类,并且为泛型类,带类型参数T,这样,你希望增量加载哪些类型的数据,就直接从该抽象类派生自己的类就可以了,你也可以根据实际情况来完善扩充。

    public abstract class LoadMoreBase<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
/// <summary>
/// 此属性表示是否还有可加载的项。
/// </summary>
public bool HasMoreItems
{
get
{
return HasMoreItemsCore;
}
} /// <summary>
/// 该属性可在派生类中重写。
/// </summary>
protected abstract bool HasMoreItemsCore { get; } public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
return AsyncInfo.Run(c => LoadMoreItemsAsyncCore(c, count));
} /// <summary>
/// 该方法可在派生类中重写。
/// </summary>
protected abstract Task<LoadMoreItemsResult> LoadMoreItemsAsyncCore(CancellationToken cancel, uint count);
}

前面说过,LoadMoreItemsAsync方法返回的是IAsyncOperation<TResult>,需要通过Task来转换,所以,要准备一个返回Task的LoadMoreItemsAsyncCore方法,这个方法声明为抽象方法,让派生类去实现它。

同样,HasMoreItems属性包装了HasMoreItemsCore属性,也声明为抽象,让派生类去实现它。

有人说,示例太复杂了会伤人品,所以这个示例老周不打算把它弄得太复杂,就简单一点吧,模拟一下网络读取数据时的延迟加载,并且数据项为随机生产的整数。

现在,实现刚才的抽象类。

    public class RandomNumbersCollection : LoadMoreBase<int>
{
/// <summary>
/// 构造函数
/// </summary>
public RandomNumbersCollection()
{
rand = new Random();
} protected override bool HasMoreItemsCore
{
get
{
return Count < MAX_NUM;
}
} // 最大项目数
const int MAX_NUM = ; Random rand = null; //用于产生随机数 protected override async Task<LoadMoreItemsResult> LoadMoreItemsAsyncCore(CancellationToken cancel, uint count)
{
LoadMoreItemsResult res = new LoadMoreItemsResult();
// 开始加载
this.LoadMoreStarted?.Invoke(this, EventArgs.Empty);
// 如果操作已处于取消状态,则不再加载项
if (cancel.IsCancellationRequested)
{
res.Count = ;
}
else
{
// 向集合中添加指定项
for (uint n = ; n < count; n++)
{
// 模拟延迟加载
await Task.Delay();
int newItem = rand.Next();
Add(newItem);
// 再次判断项数目是否超出最大值
if (Count >= MAX_NUM)
{
break;
}
}
}
// 完成加载
this.LoadMoreEnd?.Invoke(this, EventArgs.Empty);
return res;
} #region 公共事件
/// <summary>
/// 该事件在开始加载时发生。
/// </summary>
public event EventHandler LoadMoreStarted;
/// <summary>
/// 该事件在加载完成后发生。
/// </summary>
public event EventHandler LoadMoreEnd;
#endregion
}

厚道一点,数据别太多,这里我就设定900条数据吧,多了对身体健康有害。然后,在加载过程中,用Task.Delay方法故意拖延200毫秒,以模拟网络延迟现象。

在这个类中,我还加了两个事件,当开始增量加载时发生LoadMoreStarted事件,当加载完成时发生LoadMoreEnd事件。为啥要弄这两个事件呢,就是为了可以跟UI互动,在用户界面的代码中可以处理这两个事件,以完成在进行增量加载时提示用户。比如,在界面上显示“正在提娶,请稍候……”,哦,不对,是“正在提取……”。

一切就绪,咱们就在UI上试试吧。XAML代码如下。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView Name="lv" /> <Grid Canvas.ZIndex="10" Name="msgloading" Visibility="Collapsed">
……
</Grid>
</Grid>

支持增量加载的控件只有ListView和GridView。名为msgloading的Grid主要是用来在增量加载操作时显示提示信息。

下面代码将ListView的数据源设置为刚才定义的集合实例。

            BaseColl.RandomNumbersCollection src = new BaseColl.RandomNumbersCollection();
src.LoadMoreStarted += Src_LoadMoreStarted;
src.LoadMoreEnd += Src_LoadMoreEnd;
lv.ItemsSource = src; …… private void Src_LoadMoreEnd(object sender, EventArgs e)
{
this.msgloading.Visibility = Visibility.Collapsed;
} private void Src_LoadMoreStarted(object sender, EventArgs e)
{
this.msgloading.Visibility = Visibility.Visible;
}

这样就可以了,不信,你运行试试。结果如下图。

基本效果已经有了,下面还得介绍一下,ListViewBase有个属性叫DataFetchSize,这个属性有点重要,因为它可以用来控制每次增量加载操作的量。如果量太大了,加载时间过长,会影响应用总体效果,所以这个值可以适当设置。

那么,这个加载量是如何计算的呢?是按页面算的,比如DataFetchSize="2"就表示每次增量加载时会提取2个页面的数据量。假如一个页面有5条数据,2个页面就是提取10条。如果是DataFetchSize="4",每次加载就会提取5*4=20条数据。这个属性的默认值为3,即提取15条数据。

那么,这一个页面有多少条数据又是如何确定的呢?由你的“视野”决定。请看下图。

在这个窗口大小内,你数一下,你能看到多少条数据?是不是7条,如果是7条,那么页面的大小为7,若DataFetchSize属性的值为2,即:每次增量加载要提取的数据量为2*7=14。

再看下图。

你数数看,在这个窗口大小范围内,你是不是看到10条数据,如果是,页面的大小就为10,如果DataFetchSize的值为3,表示它每次会提取3个页面的数据量,即3*10=30条数据。

知道这个方法后,你应该按照具体的情况来设置DataFetchSize属性的值了,尽量不要太大,以免加载的量太大而消耗过多的时间。

好了,肚子开始饿了,老周的饭也该好了,准备开饭了。

示例源代码下载

【Win10 应用开发】实现数据的增量加载的更多相关文章

  1. 背水一战 Windows 10 (57) - 控件(集合类): ListViewBase - 增量加载, 分步绘制

    [源码下载] 背水一战 Windows 10 (57) - 控件(集合类): ListViewBase - 增量加载, 分步绘制 作者:webabcd 介绍背水一战 Windows 10 之 控件(集 ...

  2. 如何每日增量加载数据到Hive分区表

    如何每日增量加载数据到Hive分区表 hadoop hive shell crontab 加载数据 数据加载到Hive分区表(两个分区,日期(20160316)和小时(10))中 每日加载前一天的日志 ...

  3. Andriod开发技巧——Fragment的懒加载

    我们在做应用开发的时候,一个Activity里面可能会以viewpager(或其他容器)与多个Fragment来组合使用,而如果每个 fragment都需要去加载数据,或从本地加载,或从网络加载,那么 ...

  4. 第五篇 Integration Services:增量加载-Deleting Rows

    本篇文章是Integration Services系列的第五篇,详细内容请参考原文. 在上一篇你学习了如何将更新从源传送到目标.你同样学习了使用基于集合的更新优化这项功能.回顾增量加载记住,在SSIS ...

  5. 第四篇 Integration Services:增量加载-Updating Rows

    本篇文章是Integration Services系列的第四篇,详细内容请参考原文. 回顾增量加载记住,在SSIS增量加载有三个使用案例:1.New rows-add rows to the dest ...

  6. 第三篇 Integration Services:增量加载-Adding Rows

    本篇文章是Integration Services系列的第三篇,详细内容请参考原文. 增量加载是什么增量加载仅加载与先前加载差异的.差异包括:->新增的行->更新的行->删除的行通过 ...

  7. AngularJS进阶(三十)AngularJS项目开发技巧之图片预加载

    AngularJS项目开发技巧之图片预加载 绪 项目(移动端采用Ionic 框架)开发完毕,测试阶段发现移动APP首页的广告图片(图片由服务器端返回相应url地址)很难加载,主要原因还是网速.如下图左 ...

  8. OGG初始化之将数据从文件加载到Replicat

    要使用Replicat建立目标数据,可以使用初始加载Extract从源表中提取源记录,并将它们以规范格式写入提取文件.从该文件中,初始加载Replicat使用数据库接口加载数据.在加载过程中,更改同步 ...

  9. 【译】第五篇 Integration Services:增量加载-Deleting Rows

    本篇文章是Integration Services系列的第五篇,详细内容请参考原文. 在上一篇你学习了如何将更新从源传送到目标.你同样学习了使用基于集合的更新优化这项功能.回顾增量加载记住,在SSIS ...

随机推荐

  1. 用dubbo时遇到的一个序列化的坑

    首先,这是标题党,问题并不是出现在序列化上,这是报错的一部分: Caused by: com.alibaba.dubbo.remoting.RemotingException: Failed to s ...

  2. 哪种缓存效果高?开源一个简单的缓存组件j2cache

    背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...

  3. JavaScript对象和数组

    1.JavaScript中有两个非常重要的数据类型是对象和数组. 通过"."或者"[]"来访问对象属性 举例:var book = { topic:" ...

  4. 听H3絮叨:何以让天下没有难用的流程

    最近朋友圈.网站新闻铺天盖地是"让天下没有难用的流程",有人就要问了,H3 BPM何德何能,为BPM站台,让天下没有难用的流程? 这是一个关于"办公室空想"的故 ...

  5. sql server 取文件名函数 转载

    /****** Object: UserDefinedFunction [dbo].[GetDirectoryPath] Script Date: 2016-12-16 16:54:05 ****** ...

  6. BZOJ1012: [JSOI2008]最大数maxnumber [线段树 | 单调栈+二分]

    1012: [JSOI2008]最大数maxnumber Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 8748  Solved: 3835[Submi ...

  7. 【python之路5】学习小结

    一.编程语言 java C语言 C++ C# Python 二.python语言的种类 Cpython:python的官方版本,使用最为广泛,实现将python(py文件)转换为字节码文件(pyc文件 ...

  8. JS中给正则表达式加变量

    前不久同事询问我js里面怎么给正则中添加变量的问题,遂写篇博客记录下.   一.字面量 其实当我们定义一个字符串,一个数组,一个对象等等的时候,我们习惯用字面量来定义,例如: var s = &quo ...

  9. FastClick 填坑及源码解析

    最近产品妹子提出了一个体验issue —— 用 iOS 在手Q阅读书友交流区发表书评时,光标点击总是不好定位到正确的位置: 如上图,具体表现是较快点击时,光标总会跳到 textarea 内容的尾部.只 ...

  10. 【腾讯优测干货分享】Android内存泄漏的简单检查与分析方法

    本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d14047603a5bf1242ad01b 导语 内存泄漏问题大约是An ...