美文网首页.NETdotNETUWP开发
UWP ListView控件分组显示数据

UWP ListView控件分组显示数据

作者: 鳗驼螺 | 来源:发表于2019-05-17 12:54 被阅读0次

    本文介绍如何使用 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

    相关文章

      网友评论

        本文标题:UWP ListView控件分组显示数据

        本文链接:https://www.haomeiwen.com/subject/qtxnaqtx.html