惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
H
Help Net Security
F
Fortinet All Blogs
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 司徒正美
量子位
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recorded Future
Recorded Future
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
AI
AI
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
U
Unit 42
V2EX - 技术
V2EX - 技术
MongoDB | Blog
MongoDB | Blog
Schneier on Security
Schneier on Security
博客园 - Franky
H
Heimdal Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
W
WeLiveSecurity
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
B
Blog RSS Feed
N
News | PayPal Newsroom
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
Hacker News - Newest:
Hacker News - Newest: "LLM"
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
雷峰网
雷峰网

寒九

Service Worker缓存图片 – 寒九 ASP.NET Core中使用EF Core实现部分更新实体记录 – 寒九 微软商店开发者账户申请 – 寒九 数据库结构管理利器——数据库迁移(数据库模式迁移) – 寒九 JPEG/JPG/JFIF/TIFF/EXIF格式解析 – 寒九 音乐播放器核心设计——播放列表设计 – 寒九 LeetCode-30 串联所有单词的子串题解 – 寒九 LeetCode-10 正则表达式匹配题解 – 寒九 Async方法导致的死锁与无响应 – 寒九
WinUI 3与UWP中GridView和ListView的性能优化 – 寒九
HHao 文章: 41 · 2024-04-07 · via 寒九

本文内容参考微软文档

列表视图和网格视图 – Windows apps | Microsoft Learn
ListView 和 GridView UI 优化 – UWP applications | Microsoft Learn

WinUI 3中提供了几种不同的集合控件,包括ListView、GridView、ListBox、ItemsView、ItemsRepeater、ItemsControl,这几种控件都是用来显示集合的,功能看起来类似,可能很多人会不知道该选用哪种控件,这里简单说一下。

ListView和GridView都是继承于ListViewBase类,都提供有表头表尾等功能,区别在于二者呈现集合的布局。ListView主要呈现方式为垂直排列,相当于是正常的列表,适用于歌单、联系人等场景;GridView主要呈现方式为网格布局,它的呈现方式类似于Grid布局,从左到右依次排列,一行放满了就添加一行继续排列,适用于卡片排列、图库、产品库等场景。

需要注意的是,在GridView中,如果一行中剩余的控件不够一个元素排布,那么就会把这些空间空出来,并不会自动放缩已有元素来把空间占满,若要达到这种效果,UWP可以使用Windows Community Toolkit提供的AdaptGridView(7.x版本提供,8.0开始似乎不再提供,可采用WinUI 3的方式继续使用),WinUI 3中Windows Community Toolkit不提供此控件,但可直接复制AdaptGridView源码使用,目前未发现严重兼容问题。

ListBox虽然与ListView相似,但是它主要作用实际上是为下拉框等场景提供呈现组件,并不能很好地处理大量复杂数据,因此不推荐作为列表控件使用,仅在需要显示少量元素时使用最好。

ItemsView相比ListView和GridView提供了更加灵活的布局方式,它允许你更换多种不同的布局,换句话说支持你自己实现布局(只要你实现了Layout 类),目前已提供了多种布局,支持虚拟化的布局有LinedFlowLayoutStackLayoutUniformGridLayout,不支持虚拟化的布局有ColumnMajorUniformToLargestGridLayout,具体的布局实现可参考Layout 类API文档中的“派生”部分。

ItemsRepeater和ItemsView一样,同样支持多种布局,但是它更加纯粹,它默认不提供任何默认界面,所以也不提供任何选择、点击等事件,若不需要这些功能,则可使用ItemsRepeater,它相对来说更轻量。

ItemsControl同ItemsRepeater类似,都是更加轻量的存在,它是ListViewGridViewListBox、其他 Selector 派生控件 (ComboBox、 FlipView) 、 MenuFlyoutPresenter等控件的基类,虽然是这些集合的基类,但是它自身也可直接使用,直接使用时则不支持虚拟化,建议还是使用ItemsRepeater。

WinUI 3(WindowsAppSDK 1.5)中提供的集合控件的关系图
虚拟化选择
SelectionMode
点击元素
IsItemClickEnabled
更换布局
Layout
渐进更新
ItemsRepeater
ItemsView
(IsItemInvokedEnabled)
ItemsControl
ListBox
ListView
GridView
几种集合控件的比较

注意,虽然ItemsControl并不支持更换布局,但是它提供了ItemsPanel属性用于修改元素的布局,也能在一定程度上更换布局,只是该属性要求提供的是Panel的派生类,因此相比较ItemsView等灵活性不是特别大。

2、UI虚拟化

UI虚拟化简单来说指的是只渲染可见范围内的元素,不在可见范围内的元素会被删除或者重复利用,在UI界面开发中非常常用,在面对大批量数据时可以节省程序所占用的内存,是性能优化中最佳的手段之一。

本文主要讲ListView、GridView的性能优化,其它控件同理,后文统称容器,对于具体的控件则会单独说明。

WinUI 3中很多集合控件都支持UI虚拟化,且默认启用,因此不需要额外配置即可使用。但是,在某些情况下,虚拟化可能会失效,因此要确保编写的代码不会触发虚拟化失效才能正常利用UI虚拟化。

如前所述,UI虚拟化只会渲染可见范围内的元素,实际上为了用户体验,还会包括当前可见范围的前后一段范围内的元素也会被保留和渲染。有一个专门的概念来描述可见范围——视口(ViewPort)。在元素即将进入视口时,程序会自动创建对应的实际元素并开始渲染,在元素离开视口后,并且不太可能很快就再次出现时,对应的实际元素将会被删除。

为了确定视口,程序会自动根据容器的宽高进行计算,这里就是一个常见的导致虚拟化失效的地方了。有些面板允许子元素拥有无限的宽度或高度,例如ScrollViewer,这样一来,容器的宽度或高度为无限大,视口自然也被认为是无限大,于是容器内所有的元素都被认为在视口内,从而会把所有的实际元素渲染出来,此时虚拟化就失效了。因此,务必要确保容器有确定的宽度和高度,并且不会过度超出实际呈现的区域。

此外,若要提供其它面板(ItemsPanel)修改子元素布局,则必须使用虚拟化面板,例如ItemsWrapGridItemsStackPanel。如果使用 VariableSizedWrapGridWrapGrid或 StackPanel,则不会实现虚拟化。

另外,某些元素的控件涉及到图片或其它可变宽高的控件,导致无法确定具体大小,因此可能也会导致计算出的视口内所需元素数量过大进而浪费内存,推荐最好能提供具体大小或是最大/最小大小。

此外,还有两种不同的虚拟化策略,即增量加载或分页查看,它们的核心思想都是减少一次性加载的元素数量,从而减少资源占用,提高程序性能。这两种虚拟化策略和上述的虚拟化并不冲突,可叠加使用,此处由于篇幅问题不再过多赘述,可参考《ListView 和 GridView 数据虚拟化》,WinUI 3与此文档所述内容兼容,可直接使用,其中增量加载对应《增量数据虚拟化》部分,分页查看对应《随机访问数据虚拟化》部分。

3、渐进更新

ContainerContentChanging是由ListViewBase提供的功能,它允许分阶段渲染容器中的实际元素,提升加载速度,实现快速平滑滚动。但仅当ListView或GridView的ItemsPanelItemsStackPanelItemsWrapGrid时,才会引发此事件。如果将ItemsPanel替换为其它,则不会引发此事件。

分阶段渲染的含义是指,若一个元素中包含有文字、图片或其它需要异步加载的内容,则可指定不同的阶段来渲染不同的内容,例如第一阶段先渲染文字,第二阶段渲染图片,第三阶段加载异步内容,依此类推。这样,当用户快速滚动容器列表时,就不会导致所有内容全部加载和渲染出来,从而可以提高程序的性能,避免大量的渲染和回收。此处的阶段是由开发者自定义的,并不是必须要第一阶段渲染文字、第二阶段渲染图片的意思,阶段的多少也由开发者自定义。

该事件的ContainerContentChangingEventArgs参数中包含有Phase和Handled等参数,其中Phase参数用于获取当前的阶段,是一个uint类型的值,从0开始递增;Handled参数用于标记此事件是否已经被处理,可防止事件路由中的大多数处理程序再次处理同一事件,应该在第一次触发事件时修改为true。ContainerContentChangingEventArgs中还包含了其它内容,可参考文档进行查阅。

另外,ContainerContentChangingEventArgs还包含了RegisterUpdateCallback方法,这个方法可用于注册后续阶段的回调,后续阶段的回调类型与ContainerContentChanging事件处理器一致,从而可以在每个阶段处理完毕后注册下一阶段的处理器。具体代码示例参考此文档,这里不再赘述,文档中的示例已经很完善了。

4、优化元素数量

对于集合控件来说,最好的优化实际上是针对每一项子元素的实际元素控件数量进行优化,若一个元素的实际控件数量为20个,一个300项元素的集合控件实际包含的元素控件数量就超过6000个(不考虑虚拟化情况下),此时若是能够优化每项元素的控件数量,就能够极大减少总计的控件数量。例如,若优化每个元素的实际控件数量到15个,则该集合控件的实际元素控件就能减少到4500个,总计减少了1500个控件,这对于性能的优化非常可观,可以大大提升渲染速度和响应时间。

此外,针对容器中每个元素的实际元素,都对应有一个经过优化的ListViewItemPresenter,若要修改默认的各种样式(例如复选框、背景等),则可使用容器的ItemContainerStyle属性修改,具体可见此文档