Extensions in UWP Community Toolkit - ListViewExtensions
概述
UWP Community Toolkit Extensions 中有一个为 ListView 提供的扩展 - ListViewExtensions,本篇我们结合代码详细讲解 ListView Extensions 的实现。
ListViewExtensions 为每一种继承了 ListViewBase 类的控件提供了一种轻量级的方式来扩展它的附加属性。目前扩展的附加属性有 AlternateColor、AlternateItemTemplate 和 StretchItemContainerDirection;需要注意的是,扩展使用 ContainerContentChanging 事件来保证工作,如果控件的 ItemsPanel 被设置为 ItemsStackPanel 或 ItemsWrapGrid,那么扩展将不能正常工作。另外 StretchItemContainerDirection 有 Horizontal、Vertical 和 Both 三个选项。我们来看官方示例截图:
Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/extensions/listviewextensions
Namespace: Microsoft.Toolkit.Uwp.UI.Extensions; Nuget: Microsoft.Toolkit.Uwp.UI;
开发过程
代码分析
首先来看 ListView Extension - ListViewBase 的类结构:
ListViewBase.Data.cs - ListViewBase 部分类中 StretchDirection 的定义,枚举类型,有 Horizontal,Vertical 和 Both 三种类型;
ListViewBase.Properties.cs - 已标记为 Obsolete,被 ListViewExtensions 替代;
ListViewBase.cs - ListViewBase 部分类中负责事件响应处理逻辑的定义;
ListViewBaseExtensions.cs - 已标记为 Obsolete,被 ListViewBase 替代;
ListViewExtensions.cs - ListView Extension 的依赖属性定义和事件处理逻辑定义;
StretchDirection.cs - StretchDirection 的定义,枚举类型,有 Horizontal,Vertical 和 Both 三种类型;
首先来看 ListViewBase.cs 类,类中定义了 4 个PropertyChanged 事件处理方法:
OnCommandPropertyChanged(sender, args) - 重新为 listViewBase 绑定 OnItemClicked 事件;
OnAlternateColorPropertyChanged(sender, args) - 重新为 listViewBase 绑定 ColorContainerContentChanging 事件;
OnAlternateItemTemplatePropertyChanged(sender, args) - 重新为 listViewBase 绑定 ItemTemplateContainerContentChanging 事件;
ItemTemplateContainerContentChanging(sender, args) - 重新为 listViewBase 绑定 StretchItemContainerDirectionChanging 事件;
主要看一下后面三个事件的处理方法:
① ColorContainerContentChanging(sender, args):
获取当前 sender 在 container 中的索引,根据索引的奇偶数来判断,偶数则设置背景为 AlternateColor,奇数则设置背景为空;
private static void ColorContainerContentChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args) { var itemContainer = args.ItemContainer as SelectorItem; var itemIndex = sender.IndexFromContainer(itemContainer); if (itemIndex % 2 == 0) { itemContainer.Background = GetAlternateColor(sender); } else { itemContainer.Background = null; } }
② ItemTemplateContainerContentChanging(sender, args):
同样获取当前 sender 在 container 中的索引,根据索引的奇偶数来判断,偶数则设置内容模板为 AlternateItemTemplate,奇数则设置为 sender 的元素模板;
private static void ItemTemplateContainerContentChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args) { var itemContainer = args.ItemContainer as SelectorItem; var itemIndex = sender.IndexFromContainer(itemContainer); if (itemIndex % 2 == 0) { itemContainer.ContentTemplate = GetAlternateItemTemplate(sender); } else { itemContainer.ContentTemplate = sender.ItemTemplate; } }
以上两个方法,主要处理就是自定义 ListView 奇偶数元素不同的背景色和元素模板;
③ StretchItemContainerDirectionChanging(sender, args):
获取当前 sender 的 stretchDirection,如果为 Vertical 或 Both,则 container 的纵向内容对齐设置为 Stretch;如果为 Horizontal 或 Both,则 container 的横向内容对齐设置为 Stretch;
private static void StretchItemContainerDirectionChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args) { var itemContainer = args.ItemContainer as SelectorItem; var stretchDirection = GetStretchItemContainerDirection(sender); if (stretchDirection == StretchDirection.Vertical || stretchDirection == StretchDirection.Both) { itemContainer.VerticalContentAlignment = VerticalAlignment.Stretch; } if (stretchDirection == StretchDirection.Horizontal || stretchDirection == StretchDirection.Both) { itemContainer.HorizontalContentAlignment = HorizontalAlignment.Stretch; } }
接下来看 ListViewExtensions 类,首先来看类中定义的依赖属性:
AlternateColor - ListView 的备用颜色画刷,改变时触发 OnAlternateColorPropertyChanged 事件;
AlternateItemTemplate - ListView 的备用元素模板,改变时触发 OnAlternateItemTemplatePropertyChanged 事件;
StretchItemContainerDirection - 拉伸元素容器方向,改变时触发 OnStretchItemContainerDirectionPropertyChanged 事件;
来看这三个事件的处理逻辑:
① OnAlternateColorPropertyChanged(sender, args):
分别重新绑定 ColorContainerContentChanging、ColorItemsVectorChanged 和 OnListViewBaseUnloaded 事件;其中主要处理在 ColorItemsVectorChanged 中;
private static void OnAlternateColorPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { Windows.UI.Xaml.Controls.ListViewBase listViewBase = sender as Windows.UI.Xaml.Controls.ListViewBase; if (listViewBase == null) { return; } listViewBase.ContainerContentChanging -= ColorContainerContentChanging; listViewBase.Items.VectorChanged -= ColorItemsVectorChanged; listViewBase.Unloaded -= OnListViewBaseUnloaded; _itemsForList[listViewBase.Items] = listViewBase; if (AlternateColorProperty != null) { listViewBase.ContainerContentChanging += ColorContainerContentChanging; listViewBase.Items.VectorChanged += ColorItemsVectorChanged; listViewBase.Unloaded += OnListViewBaseUnloaded; } }
ColorItemsVectorChanged(sender, args) 方法对于 ColorContainerContentChanging 方法中未处理的 Insert 和 Remove 场景做处理,获取当前的 ListViewBase,遍历每个元素,为元素容器设置背景,依据是元素的索引;ColorContainerContentChanging 方法的处理同样是根据元素的索引奇偶数来设置背景;
private static void ColorItemsVectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs args) { // If the index is at the end we can ignore if (args.Index == (sender.Count - 1)) { return; } // Only need to handle Inserted and Removed because we'll handle everything else in the // ColorContainerContentChanging method if ((args.CollectionChange == CollectionChange.ItemInserted) || (args.CollectionChange == CollectionChange.ItemRemoved)) { _itemsForList.TryGetValue(sender, out Windows.UI.Xaml.Controls.ListViewBase listViewBase); if (listViewBase == null) { return; } int index = (int)args.Index; for (int i = index; i < sender.Count; i++) { var itemContainer = listViewBase.ContainerFromIndex(i) as Control; if (itemContainer != null) { SetItemContainerBackground(listViewBase, itemContainer, i); } } } }
② OnAlternateItemTemplatePropertyChanged(sender, args):
分别重新绑定 ItemTemplateContainerContentChanging 和 OnListViewBaseUnloaded 事件,处理主要是根据元素的索引的奇偶数,设置元素模板;
private static void OnAlternateItemTemplatePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { Windows.UI.Xaml.Controls.ListViewBase listViewBase = sender as Windows.UI.Xaml.Controls.ListViewBase; if (listViewBase == null) { return; } listViewBase.ContainerContentChanging -= ItemTemplateContainerContentChanging; listViewBase.Unloaded -= OnListViewBaseUnloaded; if (AlternateItemTemplateProperty != null) { listViewBase.ContainerContentChanging += ItemTemplateContainerContentChanging; listViewBase.Unloaded += OnListViewBaseUnloaded; } }
③ OnStretchItemContainerDirectionPropertyChanged(sender, args):
分别重新绑定 StretchItemContainerDirectionChanging 和 OnListViewBaseUnloaded 事件,处理是获取当前 itemContainer 的 stretchDirection,如果为 Vertical 或 Both,则 container 的纵向内容对齐设置为 Stretch;如果为 Horizontal 或 Both,则 container 的横向内容对齐设置为 Stretch;
private static void OnStretchItemContainerDirectionPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { Windows.UI.Xaml.Controls.ListViewBase listViewBase = sender as Windows.UI.Xaml.Controls.ListViewBase; if (listViewBase == null) { return; } listViewBase.ContainerContentChanging -= StretchItemContainerDirectionChanging; listViewBase.Unloaded -= OnListViewBaseUnloaded; if (StretchItemContainerDirectionProperty != null) { listViewBase.ContainerContentChanging += StretchItemContainerDirectionChanging; listViewBase.Unloaded += OnListViewBaseUnloaded; } }
调用示例
我们把 AlternateColor 设置为浅灰色,AlternateItemTemplate 中的文字前景色设置为橙色,StretchItemContainerDirection 设置为 Both;可以看到运行显示中 test01 test03 这些元素的显示和设置是一致的。
<Page.Resources> <DataTemplate x:Name="NormalTemplate"> <TextBlock Text="{Binding Title}" Foreground="Green"></TextBlock> </DataTemplate> <DataTemplate x:Name="AlternateTemplate"> <TextBlock Text="{Binding Title}" Foreground="Orange"></TextBlock> </DataTemplate></Page.Resources><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ListView x:Name="SampleListView" Margin="12" ItemTemplate="{StaticResource NormalTemplate}" extensions:ListViewExtensions.AlternateColor="LightGray" extensions:ListViewExtensions.AlternateItemTemplate="{StaticResource AlternateTemplate}" extensions:ListViewExtensions.StretchItemContainerDirection="Both"> </ListView></Grid>
总结
到这里我们就把 UWP Community Toolkit Extensions 中的 ListViewExtensions 的源代码实现过程和简单的调用示例讲解完成了,希望能对大家更好的理解和使用这个扩展有所帮助。欢迎大家多多交流,谢谢!
最后,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通过微博关注最新动态。
衷心感谢 UWPCommunityToolkit 的作者们杰出的工作,Thank you so much, UWPCommunityToolkit authors!!!
- 点赞
- 收藏
- 关注作者
评论(0)