ItemsRepeater的阅读笔记。
ItemsRepeater和ItemsControl有点类似,都提供了用于处理元素集合的逻辑。和ItemsControl相比,ItemsRepeater支持UI布局的虚拟化。但是ItemsRepeater必须使用外部的数据源,无法像ItemsControl那样,可以有拥有自己的数据集合。
ItemsRepeater对需要处理的元素不做预设,需要用户自己指定一个item模板来展示元素。此外还需要指定一个布局来决定元素的大小和位置。通过data template选择器,可以对不同类型的元素设置不同的模板。ItemsRepeater可以看作定制化更强的ListView和GridView。
ItemsRepeater不是从Control派生出来的,所以没有Control Template。自己是不带任何内置的滚动控制的。如果需要滚动控制,需要在ItemsRepeater外面套一层ScrollViewer。如果1809的Win10之前,需要在外面再套一个ItemsRepeaterScrollHost:
<muxc:ItemsRepeaterScrollHost>
<ScrollViewer>
<muxc:ItemsRepeater ... />
</ScrollViewer>
</muxc:ItemsRepeaterScrollHost>
主要是因为1809之前ScrollViewer没有实现ItemsRepeater所需的IScrollAnchorProvider接口。
ItemsRepeater的使用者需要指定ItemsSource、ItemTemplate来指挥元素的显示。ItemsSource可以绑定到一个Observable容器,而ItemTemplate可以是一个DataTemplate或者DataTemplateSelector。
下面是一个作用于String元素的DataTemplate的例子:
<DataTemplate x:DataType="x:String">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="47"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="Assets/placeholder.png" Width="32" Height="32"
HorizontalAlignment="Left"/>
<TextBlock Text="{x:Bind}" Foreground="Teal"
FontSize="15" Grid.Column="1"/>
</Grid>
</DataTemplate>
由于指定DataTemplate是经常性行为,所以你可以直接DataTemplate作为ItemsRepeater的子元素:
<ItemsRepeater ItemsSource="{x:Bind Items}">
<DataTemplate>
<!-- ... -->
</DataTemplate>
</ItemsRepeater>
不像ListView或者其他容器,ItemsRepeater不会在一个封装元素的DataTemplate之外再套一层容器,比如ListViewItem。这层额外的容器主要是涵盖默认的策略,比如内外间距,选项的视觉展示,指针悬停的视觉展示等等。 不过你可以再DataTemplate里面显示使用ListViewItem等容器。如果你的Collection是控件,比如Button,那么可以使用ContentPresenter来显示这些控件。
如果要显示的元素不是同一种类型,那么需要采用 DataTemplateSelector:
<ItemsRepeater ...>
<ItemsRepeater.ItemTemplate>
<local:VariableSizeTemplateSelector Large="{StaticResource LargeItemTemplate}"
Small="{StaticResource SmallItemTemplate}"/>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
通过自定义DataTemplateSelector的SelectTemplateCore(Object)来插入选择相关的逻辑。
也可以通过让ItemTemplate实现Windows.UI.Xaml.Controls.IElementFactory来按需创建元素。这是更高级的用法。
Configure the data source
那么,可以给ItemsRepeater的ItemsSource注射什么样类型的数据源呢?
- IEnumerable/IIterable (默认必须有)
- IReadonlyList/IVectorView (允许通过Index访问数据)
- IList/IVector
- INotifyCollectionChanged (允许观察数据源改动)
- IObservableVector (同上,但是不支持Move操作,会导致移动的时候丢失焦点)
- IKeyIndexMapping (支持在Reset的情况下,减少对元素的重新获取操作)
下面三个接口在ListView中用到,但是对ItemsRepeater没有影响:
- ISupportIncrementalLoading
- IIemsRangeInfo
- ISelectionInfo
对于ISupportIncrementalLoading,一个变通的做法是通过对ScrollViewer的滚动进行观察,然后加载所需的元素。例子在文档中。
Change the layout of items
ItemsRepeater选哟一个Layout对象,来管理所有元素的UI呈现。可以使用StackLayout或者UniformGridLayout。默认情况下,ItemsRepeater使用的是竖排的StackLayout。
StackLayout把元素排布成一行或者一列。可以设置Spacing辖属来指定元素间隔;设置Orientation辖属来指定元素的走向。下面是一个例子:
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<muxc:ItemsRepeater ItemsSource="{x:Bind Items}" ItemTemplate="{StaticResource MyTemplate}">
<muxc:ItemsRepeater.Layout>
<muxc:StackLayout Orientation="Horizontal" Spacing="8"/>
</muxc:ItemsRepeater.Layout>
</muxc:ItemsRepeater>
UniformGridLayout可以让元素朝某个方向排布,但是可以折行。折行的标准是最小宽度,或者最小高度。你可以指定最小的MinItemHeight或者MinItemWidth,如果不指定,那么第一个条目的测量而来的尺寸会被作为最小尺寸。当然也可以使用MinColumnSpacing和MinRowSpacing来指定间距。通过ItemsStretch和ItemsJustification可以指定如何使用多余的空白。
ItemsStretch可以设置为以下内容:
- None,不适用剩余空白
- Fill,在一个方向使用剩余空白
- Uniform,在连个方向使用剩余空白,保持比例
ItemsStretch通过放大元素来填充空白,如果只是想移动位置的话,可以使用 ItemsJustification:
- Start,起始位置对齐,空白在折行位置
- Center,居中,空白在两边
- End,折行位置对齐,空白在起始位置
- SpaceAround,剩余空白平均添加到每个元素之前和之后
- SpaceBetween,剩余空白评价添加到每个元素之间
- SpaceEvenly,同SpaceBetween,但是开始和折行位置也能获得空白
ItemsStretch辖属影响的是measure过程,而ItemsJustification影响的是arrange过程。
下面是使用UniformGridLayout的一个例子:
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<muxc:ItemsRepeater ItemsSource="{x:Bind Items}"
ItemTemplate="{StaticResource MyTemplate}">
<muxc:ItemsRepeater.Layout>
<muxc:UniformGridLayout MinItemWidth="200"
MinColumnSpacing="28"
ItemsJustification="SpaceAround"/>
</muxc:ItemsRepeater.Layout>
</muxc:ItemsRepeater>
Lifecycle events
ItemsRepeater是一个虚拟化的控件。不能只依赖于Loaded/Unloaded事件来判断元素是否从视觉树中移除。因为有可能元素只是被回收了。所以ItemsRepeater提供了其他事件:
- ElementPrepared,在一个元素将被使用的时候触发。
- ElementClearing,当元素被回收的时候触发。
- ElementIndexChanged,在元素索引发生变化的时候触发。
Sorting, Filtering and Resetting the Data
在ItemsSource进行了Reset之后,ItemsRepeater需要从0开始构建UI。但是如果ItemsSource支持IKeyIndexMapping,ItemsRepeater就可以快速检测:
- Reset之前和之后都使用到的UIElements
- 需要被删除的元素
- 需要被添加的元素
这样就可以避免Reset之后从0开始构建UI了。
Create a custom collection control
这个章节讲如何创建一个包含ItemsRepeater的自定义控件。采用的手段是在自定义控件的ControlTemplate中指定ItemsRepeater。
也可以派生ItemsControl来达到相似的功能,跟包含ItemsRepeater的方法相比,这是继承优先还是组合优先的问题。
Display grouped items
说的是如何嵌套ItemsRepeater,也就是在一个ItemsRepeater的ItemTemplate中指定其他ItemsRepeater。
Bringing an Element Into View
让一个元素进入显示范围。XAML在 1)收到键盘焦点 2)收到讲述者焦点的时候会自动将让目标元素进入显示范围。其他情况下,需要做一些额外的动作:
- 实例化目标元素对应的UIElement
- 执行布局操作,保证元素有一个有效的位置
- 发送一个请求,把已实例化的元素带入显示范围
Enable Accessibility
默认情况下ItemsRepeater不提供辅助访问支持。关于辅助访问,可以参考Usability for Windows apps。如果你通过组合的方式,从ItemsRepeater创建了一个新的控件,那么可以参考Custom automation peers来提供此控件的辅助访问性。
默认情况下,ItemsRepeater提供键盘操作,基于2D Directional Navigation for Keyboarding。ItemsRepeater的XYFocusKeyboardNavigation模式默认是开启的。
ItemsRepeater会确保它的tab order是正确的。默认情况下TabFocusNavigation属性设置是Once。
如果要支持Announcing "Item X of Y" in Screen Readers
,那么要确保PositionInSet和SizeOfSet在条目增删的时候保持最新。文档中有一个对应的例子。
(完)