UWP ListView控件分组显示数据

本文介绍如何使用 ListView 分组显示数据。这里使用ListView来实现一个简单的联系人列表,并将联系人按首字母分组显示,效果如图:

listview_contacts_group.png

一、定义数据类

(一)联系人类 Contact

Contact类只有二个属性,名字和号码。Name表示联系人的姓名,姓名的首字母就是后面我们将用来分组联系人数据的Key

class Contact
{
    public string Name { get; set; }
    public string Phone { get; set; }
}

(二)联系人组 ContactGroup

这个类需要实现IGrouping接口:

public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable

实现这个接口时,要指定TKeyTElement的类型。TKey就是对数据分组的Key,我们要用联系人姓名的首字母来分组数据,首字母是个char(当然,用String也可以,这二者很容易转换),所以这里TKey的类型设定为char,而TElement是数据本身的类型,我们的数据是联系人,所以TElement的类型就是Contact

所以最终ContactGroup实现的接口类型为IGrouping<char, Contact>

class ContactGroup : IGrouping<char, Contact>
{
    private readonly IEnumerable<Contact> contacts;

    public ContactGroup(char key, IEnumerable<Contact> contacts)
    {
        Key = key;
        this.contacts = contacts;
    }

    public char Key { get; }

    public IEnumerator<Contact> GetEnumerator() => contacts.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => contacts.GetEnumerator();
}

这里要实现接口的一个属性和二个方法:

  • Key属性:只读,数据分组依据的对象,本例按姓名首字母排序,那么这里就是指联系人的首字母,所以是个char类型;当然,根据不同的分组要求,可以设定为任何类型对象,比如,如果我们的Contact类中还保存了联系人的生日(DateTime Birthday),我们想用生日来排序联系人,那么这里的Key就是DateTime类型,当然,此时ContactGroup实现的接口类型要改为IGrouping<DateTime, Contact>
  • GetEnumerator()方法: 这二个方法都直接返回所有联系人(contacts)的迭代器,contacts是该分组中包含的所有联系人。

二、分组数据

这里定义一组联系人的示例数据:

var contacts = new Contact[]
{
    new Contact(){ Name = "Mand", Phone = "16812345678"},
    new Contact(){ Name = "Mike", Phone = "16887654321"},
    new Contact(){ Name = "Jack", Phone = "16812121212"},
    new Contact(){ Name = "Jobs", Phone = "16800008888"},
    new Contact(){ Name = "Json", Phone = "16887654321"},
    new Contact(){ Name = "Rose", Phone = "16888888888"},
};

然后将这些联系人分组:

var contactsGroup = contacts.GroupBy(contact => contact.Name.First(), (key, list) => new ContactGroup(key, list));

这里使用GroupBy的下面这个重载来生成分组后的数据:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector);

这里keySelectorcontact => contact.Name.First())用来生成每个联系人的排序的键。前面说了,我们要按联系人的名字首字母排序,所以这里使用contact.Name.First()来生成它的首字母,它的类型是个char,所以这里生成的就是char类型的键(同时这个键也作为后面的resultSelector中的key参数值),resultSelector(key, list) => new ContactGroup(key, list))用来生成每个组的分组对象(ContactGroup对象,参数keykeySelector中生成的排序对象,list是这个分组中包含的联系人数据),最终得到的contactsGroup对象的类型也就是IEnumerable<ContactGroup>

思考:

我们按姓名首字母来分组联系人时,默认情况下是按首字母升序排列的,那么能不能按首字母降序排列呢?这个只需要对生成的contactsGroup进行排序即可,如要按首字母降序排列:

contactsGroup = contactsGroup.OrderByDescending(group => group.Key);

以此类推,还可以做其它要求的排序。

三、Xaml页面显示数据

现在可以在xaml页面来显示分组数据了。

(一)定义 CollectionViewSource 资源

首先在<Page.Resources>定义一个CollectionViewSource资源,标识符设置为CSVContacts,并将其IsSourceGrouped属性设置为True,表明数据已经分组了 。ListView的分组数据需要通过CollectionViewSource来显示。

<Page.Resources>
    <CollectionViewSource x:Name="CvsContacts" x:Key="CvsContacts" IsSourceGrouped="True"/>
</Page.Resources>

(二)定义 ListView

1、绑定数据

<ListView ItemsSource="{x:Bind CvsContacts.View, Mode=OneWay}"></ListView>

ListViewItemsSource 不能直接绑定到联系人数据(否则就不会分组显示),而是要绑定到CSvContacts.View,然后在.cs代码中,设置CsvContacts.Source来加载数据,这里就是将前面生成的联系人分组数据设置给它:

CvsContacts.Source = contactsGroup;

2、显示分组Header信息

<ListView.GroupStyle>
    <GroupStyle>
        <GroupStyle.HeaderTemplate>
            <DataTemplate x:DataType="local:ContactGroup">
                <TextBlock Text="{x:Bind Key}"/>
            </DataTemplate>
        </GroupStyle.HeaderTemplate>
    </GroupStyle>
</ListView.GroupStyle>

Header(组眉)用来显示分组对象(ContactGrouplocal:ContactGroup)的信息,本例就用来显示分组的Key{x:Bind Key}),也就是各分组中姓名共同的首字母。

3、显示列表项信息

<ListView.ItemTemplate>
    <DataTemplate x:DataType="local:Contact">
        <StackPanel Orientation="Horizontal" Margin="12,0,0,0">
            <SymbolIcon Symbol="Phone"/>
            <TextBlock Text="{x:Bind Name}" FontWeight="Bold" Margin="8,0,8,0"/>
            <TextBlock Text="{x:Bind Phone}"/>
        </StackPanel>
    </DataTemplate>
</ListView.ItemTemplate>

每个列表项就是一个Contact对象,这里用来显示这个对象的信息。这里用一个SymbolIcon来显示一个电话图标,跟着用二个TextBlock分别显示联系人的姓名和其电话号码。

到这里整个示例就完成了。

三、完整代码

MainPage.cs.xaml

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
        var contacts = new Contact[]
        {
            new Contact(){ Name = "Mand", Phone = "16812345678"},
            new Contact(){ Name = "Mike", Phone = "16887654321"},
            new Contact(){ Name = "Jack", Phone = "16812121212"},
            new Contact(){ Name = "Jobs", Phone = "16800008888"},
            new Contact(){ Name = "Json", Phone = "16887654321"},
            new Contact(){ Name = "Rose", Phone = "16888888888"},
        };
        var contactsGroup = contacts.GroupBy(contact => contact.Name.First(), (key, list) => new ContactGroup(key, list));
        CvsContacts.Source = contactsGroup;
    }
}

MainPage.xaml

<Page
    x:Class="TestUWP.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TestUWP"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <CollectionViewSource x:Name="CvsContacts" x:Key="CvsContacts" IsSourceGrouped="True"/>
    </Page.Resources>

    <Grid>
        <ListView
            ItemsSource="{x:Bind CvsContacts.View, Mode=OneWay}"
            SelectionMode="None">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:Contact">
                    <StackPanel Orientation="Horizontal" Margin="12,0,0,0">
                        <SymbolIcon Symbol="Phone"/>
                        <TextBlock Text="{x:Bind Name}" FontWeight="Bold" Margin="8,0,8,0"/>
                        <TextBlock Text="{x:Bind Phone}"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
            <ListView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate x:DataType="local:ContactGroup">
                            <TextBlock Text="{x:Bind Key}"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </ListView.GroupStyle>
        </ListView>
    </Grid>
</Page>

这只是一个基本的分组方法,还有其它更好的方案,有时间再扯。

by 鳗驼螺 2019.05.17

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274

推荐阅读更多精彩内容

  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 8,839评论 3 38
  • 转 # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    吕品㗊阅读 9,620评论 0 44
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,039评论 1 32
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 4,870评论 0 9
  • 文 | 婷兮 01 更多时候,你对不起的,不是别人,而是自己。 每一个熬夜学习、通宵工作的夜晚;每一顿因忙碌而省略...
    婷兮阅读 1,748评论 3 7