背水一战 Windows 10 (57) - 控件(集合类): ListViewBase - 增量加载, 分步绘制
作者:webabcd
介绍
背水一战 Windows 10 之 控件(集合类 - ListViewBase)
- 增量加载
- 分步绘制(大数据量流畅滚动)
示例
1、ListViewBase 的增量加载
Controls/CollectionControl/ListViewBaseDemo/MyIncrementalLoading.cs
/*
* 演示如何实现 ISupportIncrementalLoading 接口,以便为 ListViewBase 的增量加载提供数据
*
*
* ISupportIncrementalLoading - 用于支持增量加载
* HasMoreItems - 是否还有更多的数据
* IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count) - 异步加载指定数量的数据(增量加载)
*
* LoadMoreItemsResult - 增量加载的结果
* Count - 实际已加载的数据量
*/ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data; namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
{
public class MyIncrementalLoading<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
// 是否正在异步加载中
private bool _isBusy = false; // 提供数据的 Func
// 第一个参数:增量加载的起始索引;第二个参数:需要获取的数据量;第三个参数:获取到的数据集合
private Func<int, int, List<T>> _funcGetData;
// 最大可显示的数据量
private uint _totalCount = ; /// <summary>
/// 构造函数
/// </summary>
/// <param name="totalCount">最大可显示的数据量</param>
/// <param name="getDataFunc">提供数据的 Func</param>
public MyIncrementalLoading(uint totalCount, Func<int, int, List<T>> getDataFunc)
{
_funcGetData = getDataFunc;
_totalCount = totalCount;
} /// <summary>
/// 是否还有更多的数据
/// </summary>
public bool HasMoreItems
{
get { return this.Count < _totalCount; }
} /// <summary>
/// 异步加载数据(增量加载)
/// </summary>
/// <param name="count">需要加载的数据量</param>
/// <returns></returns>
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
if (_isBusy)
{
throw new InvalidOperationException("忙着呢,先不搭理你");
}
_isBusy = true; var dispatcher = Window.Current.Dispatcher; return AsyncInfo.Run
(
(token) => Task.Run<LoadMoreItemsResult>
(
async () =>
{
try
{
// 模拟长时任务
await Task.Delay(); // 增量加载的起始索引
var startIndex = this.Count; await dispatcher.RunAsync
(
CoreDispatcherPriority.Normal,
() =>
{
// 通过 Func 获取增量数据
var items = _funcGetData(startIndex, (int)count);
foreach (var item in items)
{
this.Add(item);
}
}
); // Count - 实际已加载的数据量
return new LoadMoreItemsResult { Count = (uint)this.Count };
}
finally
{
_isBusy = false;
}
},
token
)
);
}
}
}
Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo3.xaml
<Page
x:Class="Windows10.Controls.CollectionControl.ListViewBaseDemo.ListViewBaseDemo3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows10.Controls.CollectionControl.ListViewBaseDemo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> <Grid Background="Transparent" Margin="10 0 10 10"> <TextBlock Name="lblMsg" /> <ListView x:Name="listView" Width="300" Height="300" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0 30 0 0">
<ListView.ItemTemplate>
<DataTemplate>
<Border Background="Blue" Width="200" CornerRadius="3" HorizontalAlignment="Left">
<TextBlock Text="{Binding Name}" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView> <TextBlock Name="lblLog" Margin="0 350 0 0" /> </Grid>
</Page>
Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo3.xaml.cs
/*
* ListViewBase(基类) - 列表控件基类(继承自 Selector, 请参见 /Controls/SelectionControl/SelectorDemo.xaml)
* IncrementalLoadingTrigger - 增量加载的触发器
* Edge - 允许触发增量加载,默认值
* None - 禁止触发增量加载
* DataFetchSize - 预提数据的大小,默认值 3.0
* 本例将此值设置为 4.0 ,其效果为(注:本例中的 ListView 每页可显示的数据量为 6 条或 7 条,以下计算需基于此)
* 1、先获取 1 条数据,为的是尽量快地显示数据
* 2、再获取 4.0 * 1 条数据
* 3、再获取 4.0 * (6 或 7,如果 ListView 当前显示了 6 条数据则为 6,如果 ListView 当前显示了 7 条数据则为 7) 条数据
* 4、以后每次到达阈值后,均增量加载 4.0 * (6 或 7,如果 ListView 当前显示了 6 条数据则为 6,如果 ListView 当前显示了 7 条数据则为 7) 条数据
* IncrementalLoadingThreshold - 增量加载的阈值,默认值 0.0
* 本例将此值设置为 2.0 ,其效果为(注:本例中的 ListView 每页可显示的数据量为 6 条或 7 条)
* 1、滚动中,如果已准备好的数据少于 2.0 * (6 或 7,如果 ListView 当前显示了 6 条数据则为 6,如果 ListView 当前显示了 7 条数据则为 7) 条数据,则开始增量加载
*
*
* 本例用于演示如何实现 ListViewBase 的增量加载(数据源需要实现 ISupportIncrementalLoading 接口,详见:MyIncrementalLoading.cs)
*/ using Windows.UI.Xaml.Controls;
using System.Linq;
using System.Collections.Specialized;
using System;
using Windows10.Common;
using Windows.UI.Xaml; namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
{
public sealed partial class ListViewBaseDemo3 : Page
{
// 实现了增量加载的数据源
private MyIncrementalLoading<Employee> _employees; public ListViewBaseDemo3()
{
this.InitializeComponent(); this.Loaded += ListViewBaseDemo3_Loaded;
} private void ListViewBaseDemo3_Loaded(object sender, RoutedEventArgs e)
{
listView.IncrementalLoadingTrigger = IncrementalLoadingTrigger.Edge;
listView.DataFetchSize = 4.0;
listView.IncrementalLoadingThreshold = 2.0; _employees = new MyIncrementalLoading<Employee>(, (startIndex, count) =>
{
lblLog.Text += string.Format("从索引 {0} 处开始获取 {1} 条数据", startIndex, count);
lblLog.Text += Environment.NewLine; return TestData.GetEmployees().Skip(startIndex).Take(count).ToList();
}); _employees.CollectionChanged += _employees_CollectionChanged; listView.ItemsSource = _employees;
} void _employees_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
lblMsg.Text = "已获取的数据量:" + _employees.Count.ToString();
}
}
}
2、ListViewBase 的分步绘制
Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo4.xaml
<Page
x:Class="Windows10.Controls.CollectionControl.ListViewBaseDemo.ListViewBaseDemo4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows10.Controls.CollectionControl.ListViewBaseDemo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> <Grid Background="Transparent"> <!--
ListViewBase(基类) - 列表控件基类
ContainerContentChanging - 数据虚拟化时,项容器的内容发生变化时触发的事件(仅 ItemsStackPanel 和 ItemsWrapGrid 有效)
--> <GridView x:Name="gridView" Margin="10 0 10 10" ContainerContentChanging="gridView_ContainerContentChanging">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Width="80" Height="80" Background="Blue">
<Rectangle x:Name="placeholderRectangle" Fill="Red" Height="10" Opacity="0" />
<TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
<TextBlock x:Name="lblAge" Text="{Binding Age}" Foreground="Aqua" />
<TextBlock x:Name="lblIsMale" Text="{Binding IsMale}" Foreground="Gray" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView> </Grid>
</Page>
Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo4.xaml.cs
/*
* ListViewBase(基类) - 列表控件基类(继承自 Selector, 请参见 /Controls/SelectionControl/SelectorDemo.xaml)
* ContainerContentChanging - 数据虚拟化时,项容器的内容发生变化时触发的事件(仅 ItemsStackPanel 和 ItemsWrapGrid 有效)
*
*
* 当 ListViewBase 的一屏需要显示的数据量极大时(一屏的 item 多,且每个 item 中的 element 也多),由于每次滚动时需要绘制当前屏的每个 element,这需要占用大量的 ui 资源,所以就会有一些卡顿
* 为了解决这个问题 uwp 给出了两种解决方案
* 1、设置 ListViewBase 的 ShowsScrollingPlaceholders 属性为 true(默认值),每次显示 item 时先显示占位符(尚不清楚怎么修改这个占位符的背景色),然后再绘制内容
* 相关演示请参见:/Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo1.xaml
* 2、通过 ListViewBase 的 ContainerContentChanging 事件,分步绘制 item 中的 element
* 本例即介绍这种方法。注意在 uwp 中已经不用这么麻烦了,可以通过 x:Bind 和 x:Phase 来实现,请参见:/Bind/PhaseDemo.xaml
*
*
* 本例用于演示如何实现 ListViewBase 的分步绘制(大数据量流畅滚动)
*
*
* 注:
* 虚拟化布局控件用于减少创建的 item 数量
* 分步绘制用于在绘制 item 时,分阶段绘制 item 上的元素
*/ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;
using Windows10.Common; namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
{
public sealed partial class ListViewBaseDemo4 : Page
{
public ListViewBaseDemo4()
{
this.InitializeComponent();
} protected override void OnNavigatedTo(NavigationEventArgs e)
{
gridView.ItemsSource = TestData.GetEmployees(); // 默认值是 true,即为了保证流畅,每次显示 item 时先会显示占位符(application 级的背景色块),然后再绘制内容
// 本例演示 ContainerContentChanging 事件的使用,所以不会用到这个
gridView.ShowsScrollingPlaceholders = false;
} private void gridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 交由我处理吧(不用系统再处理了)
args.Handled = true; // 第 1 阶段绘制
// args.Phase.ToString(); // StackPanel templateRoot = (StackPanel)args.ItemContainer.ContentTemplateRoot;
Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
TextBlock lblName = (TextBlock)templateRoot.FindName("lblName");
TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge");
TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale"); // 显示自定义占位符(也可以不用这个,而是直接显示 item 的背景)
placeholderRectangle.Opacity = ; // 除了占位符外,所有 item 全部暂时不绘制
lblName.Opacity = ;
lblAge.Opacity = ;
lblIsMale.Opacity = ; // 开始下一阶段的绘制
args.RegisterUpdateCallback(ShowName);
} private void ShowName(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 第 2 阶段绘制
// args.Phase.ToString(); // Employee employee = (Employee)args.Item;
SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
TextBlock lblName = (TextBlock)templateRoot.FindName("lblName"); // 绘制第 2 阶段的内容
lblName.Text = employee.Name;
lblName.Opacity = ; // 开始下一阶段的绘制
args.RegisterUpdateCallback(ShowAge);
} private void ShowAge(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 第 3 阶段绘制
// args.Phase.ToString(); // Employee employee = (Employee)args.Item;
SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge"); // 绘制第 3 阶段的内容
lblAge.Text = employee.Age.ToString();
lblAge.Opacity = ; // 开始下一阶段的绘制
args.RegisterUpdateCallback(ShowIsMale);
} private void ShowIsMale(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 第 4 阶段绘制
// args.Phase.ToString(); // Employee employee = (Employee)args.Item;
SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale"); // 绘制第 4 阶段的内容
lblIsMale.Text = employee.IsMale.ToString();
lblIsMale.Opacity = ; // 隐藏自定义占位符
placeholderRectangle.Opacity = ;
}
}
}
OK
[源码下载]
背水一战 Windows 10 (57) - 控件(集合类): ListViewBase - 增量加载, 分步绘制的更多相关文章
- 背水一战 Windows 10 (64) - 控件(WebView): 加载指定 HttpMethod 的请求, 自定义请求的 http header, app 与 js 的交互
[源码下载] 背水一战 Windows 10 (64) - 控件(WebView): 加载指定 HttpMethod 的请求, 自定义请求的 http header, app 与 js 的交互 作者: ...
- 背水一战 Windows 10 (56) - 控件(集合类): ListViewBase - 基础知识, 拖动项
[源码下载] 背水一战 Windows 10 (56) - 控件(集合类): ListViewBase - 基础知识, 拖动项 作者:webabcd 介绍背水一战 Windows 10 之 控件(集合 ...
- 背水一战 Windows 10 (58) - 控件(集合类): ListViewBase - ListView, GridView
[源码下载] 背水一战 Windows 10 (58) - 控件(集合类): ListViewBase - ListView, GridView 作者:webabcd 介绍背水一战 Windows 1 ...
- 背水一战 Windows 10 (55) - 控件(集合类): SemanticZoom, ISemanticZoomInformation
[源码下载] 背水一战 Windows 10 (55) - 控件(集合类): SemanticZoom, ISemanticZoomInformation 作者:webabcd 介绍背水一战 Wind ...
- 背水一战 Windows 10 (48) - 控件(集合类): FlipView
[源码下载] 背水一战 Windows 10 (48) - 控件(集合类): FlipView 作者:webabcd 介绍背水一战 Windows 10 之 控件(集合类) FlipView 示例Fl ...
- 背水一战 Windows 10 (49) - 控件(集合类): Pivot, Hub
[源码下载] 背水一战 Windows 10 (49) - 控件(集合类): Pivot, Hub 作者:webabcd 介绍背水一战 Windows 10 之 控件(集合类) Pivot Hub 示 ...
- 背水一战 Windows 10 (50) - 控件(集合类): ItemsControl - 基础知识, 数据绑定, ItemsPresenter, GridViewItemPresenter, ListViewItemPresenter
[源码下载] 背水一战 Windows 10 (50) - 控件(集合类): ItemsControl - 基础知识, 数据绑定, ItemsPresenter, GridViewItemPresen ...
- 背水一战 Windows 10 (51) - 控件(集合类): ItemsControl - 项模板选择器, 数据分组
[源码下载] 背水一战 Windows 10 (51) - 控件(集合类): ItemsControl - 项模板选择器, 数据分组 作者:webabcd 介绍背水一战 Windows 10 之 控件 ...
- 背水一战 Windows 10 (52) - 控件(集合类): ItemsControl - 自定义 ItemsControl, 自定义 ContentPresenter
[源码下载] 背水一战 Windows 10 (52) - 控件(集合类): ItemsControl - 自定义 ItemsControl, 自定义 ContentPresenter 作者:weba ...
随机推荐
- ajax 跨域请求没有带上cookie 解决办法
公司项目前后端分离.. 前端全部html 静态页面.. 后端java 接口服务 由于前后端分离,出现跨域问题. 为了解决,我们使用jsonp 方式请求接口服务,暂时解决了跨域问题(使用jquery a ...
- win7 64位远程连接oracle11g64位
1.首先下载即时客户端 instantclient-basic-windows.x64-11.2.0.4.0,下载地址:http://www.oracle.com/technetwork/topics ...
- Python 解释器介绍
举个栗子: -c cmd用法 python -c ' import os ; print ("is ok ") ' 或者(注意引号形式) python -c "impor ...
- 利用Delphi-cross-socket 库提升kbmmw 跨平台开发
以前我写过了,通过httpsys 提升windows 下,delphi 多层应用.随着delphi 10.2 对linux 的支持,很多人也想在linux 下 发布kbmmw 服务器,但是官方仅通过i ...
- windows无法卸载jdk的解决方法
装了java之后非常纠结的就是无法卸载,总不能因为卸载一个jdk去重装系统,但是看着它残存在那又非常不爽, 因为卸载会牵扯注册表等琐碎的东西,,,后来在官网发现神器一枚,此神器就是java卸载工具. ...
- JS页面跳转大全
所谓的js页面跳转就是利用javesrcipt对打开的页面ULR进行跳转,如我们打开的是A页面,通过javsrcipt脚本就会跳转到B页面.目前很多垃圾站经常用js跳转将正常页面跳转到广告页面,当然也 ...
- php多表查询数据合并,避免foreach循环嵌套
$memberList = $member->getMemberList(); $members = []; if (is_array($memberList)) { foreach ($mem ...
- Spring+mvc错误
1.2016-11-13 16:49:22 原因:@ResponseBody注解没加
- 用 ASP.NET MVC 实现基于 XMLHttpRequest long polling(长轮询) 的 Comet(转)
轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接. 优点:后端程序编写比较容易. 缺点:请求中有大半是无用,浪费带宽和服务器资源. 实例:适于小型应用. 长轮询:客 ...
- redis 部分操作
http://pan.baidu.com/s/1eRXdbUy 下载安装地址 打开一个cmd窗口,使用cd命令切换到指定目录(D:\ruanjian\redis\64bit)运行 redis-ser ...