概述

UWP Community Toolkit Extensions 中有一个为 ListView 提供的扩展 - ListViewExtensions,本篇我们结合代码详细讲解 ListView Extensions 的实现。

ListViewExtensions 为每一种继承了 ListViewBase 类的控件提供了一种轻量级的方式来扩展它的附加属性。目前扩展的附加属性有 AlternateColor、AlternateItemTemplate 和 StretchItemContainerDirection;需要注意的是,扩展使用 ContainerContentChanging 事件来保证工作,如果控件的 ItemsPanel 被设置为 ItemsStackPanel 或 ItemsWrapGrid,那么扩展将不能正常工作。另外 StretchItemContainerDirection 有 Horizontal、Vertical 和 Both 三个选项。我们来看官方示例截图:

Source: https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase

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);

     == )
    {
        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);

     == )
    {
        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
    )) { 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!!!

Extensions in UWP Community Toolkit - ListViewExtensions的更多相关文章

  1. Extensions in UWP Community Toolkit - Overview

    概述 UWP Community Toolkit  中有一个 Extensions 的集合,它们可以帮助开发者实现很多基础功能,省去自己造轮子的过程,本篇我们先来看一下 Extensions 的功能都 ...

  2. Extensions in UWP Community Toolkit - FrameworkElement Extensions

    概述 UWP Community Toolkit Extensions 中有一个为FrameworkElement 提供的扩展 - FrameworkElement Extensions,本篇我们结合 ...

  3. Extensions in UWP Community Toolkit - Mouse Cursor

    概述 UWP Community Toolkit Extensions 中有一个为 Mouse 提供的扩展 - Mouse Cursor Extensions,本篇我们结合代码详细讲解 Mouse C ...

  4. Extensions in UWP Community Toolkit - SurfaceDialTextbox

    概述 UWP Community Toolkit Extensions 中有一个为TextBox 提供的 SurfaceDial 扩展 - SurfaceDialTextbox,本篇我们结合代码详细讲 ...

  5. Extensions in UWP Community Toolkit - ViewExtensions

    概述 UWP Community Toolkit Extensions 中有一个为 View 提供的扩展 - View Extensions,本篇我们结合代码详细讲解 View Extensions  ...

  6. Extensions in UWP Community Toolkit - Visual Extensions

    概述 UWP Community Toolkit Extensions 中有一个为可视元素提供的扩展 - VisualExtensions,本篇我们结合代码详细讲解 VisualExtensions ...

  7. Extensions in UWP Community Toolkit - WebViewExtensions

    概述 UWP Community Toolkit Extensions 中有一个为 WebView 提供的扩展 - WebViewExtensions,本篇我们结合代码详细讲解 WebView Ext ...

  8. New UWP Community Toolkit

    概述 UWP Community Toolkit 是一个 UWP App 自定义控件.应用服务和帮助方法的集合,能够很大程度的简化和指引开发者的开发工作,相信广大 UWPer 并不陌生. 下面是截取自 ...

  9. Animations in UWP Community Toolkit - Overview

    概述 UWP Community Toolkit  中有一个 Animations 的集合,它们可以帮助开发者实现很多的动画,本篇我们先来看一下 Animations 的功能都有哪些,再后面会针对每一 ...

随机推荐

  1. 机器学习(1) - TensorflowSharp 简单使用与KNN识别MNIST流程

    机器学习是时下非常流行的话题,而Tensorflow是机器学习中最有名的工具包.TensorflowSharp是Tensorflow的C#语言表述.本文会对TensorflowSharp的使用进行一个 ...

  2. 单元测试er——为什么真的真的要写单元测试

    优点 为什么很多技术或者知识要说优点?因为有些道理看着很简单,大家表面上都觉得对,但是做的时候又不去做或者做不到.其中有一个很重要原因是骨子里或者潜意识并没有真实觉得这是对的,一旦想去做的时候同时会冒 ...

  3. 大数据 --> MapReduce原理与设计思想

    MapReduce原理与设计思想 简单解释 MapReduce 算法 一个有趣的例子:你想数出一摞牌中有多少张黑桃.直观方式是一张一张检查并且数出有多少张是黑桃? MapReduce方法则是: 给在座 ...

  4. SQL 数据库连续插入大批量数据时超时

    经常会处理大批量千万级的数据,一直以来都没问题.最近在处理时确出来了经常超时,程序跑一段时间就得停下来重启服务器,根据几次的调整发现了问题的所在,产生这类问题主要是以下几点所导致:      1.数据 ...

  5. C# 入门经典

    C#简介 使用.NETFrmework的含义 使用.NETFrmework的一个重要原因是他可以作为集成各种操作系统的方式 包含了一个非常大的代码库 除了提供这个库以外.NETFrmework还包含. ...

  6. 记录一个古老的Sql分页过程

    /* 根据单位ID获取排班信息 For:WXX TIme:2017-11-22 */ ALTER proc [dbo].[proc_ScheduleInfo] )='', --单位ID )='', - ...

  7. field.setAccessible(true) 简介

    今天查看别人写的代码时,发现这样一句代码,顿时来了兴趣. 需要注意setAccessible 并不是在Field中的,而是在AccessibleObject中. 下面是AccessibleObject ...

  8. Beta第五天

    听说

  9. TensorFlow问题“The TensorFlow library wasn't compiled to use SSE instructions, but these are available on your machine and could speed up CPU computations.”

    出现的问题: 在使用TensorFlow跑官方教程例子时报以下warning: 虽程序能正常跑出结果,但作为一名强迫症患者对此很是不爽,于是查找资料找到隐藏该warning的解决办法. 解决办法: 在 ...

  10. python的Collections 模块

    Collections 模块 知识点 Counter 类 defaultdict 类 namedtuple 类 在这个实验我们会学习 Collections 模块.这个模块实现了一些很好的数据结构,它 ...