Grouping In ListViews With Xamarin.Forms

In this article, we’ll see the process of grouping items in a ListView with Xamarin.Forms.

I’m creating an Address Book where we are grouping the contact’s name in the Listview by the initial character's name.

Creating the Project in VS 2017 Community

Start a new project on Visual Studio 2017, and choose File/New/Project.

We will get another window to select in Visual C# and Cross-Platform App(Xamarin.Forms).

Set the name XF_Agenda and click on OK button.

Xamarin

Select Blank App and check the platforms to which you want to generate the projects.

Check the Xamarin.Forms option, and, in Code Sharing strategy, check .NET Standard, which replaces the PCL projects option from VS 2017 update 15.5.

Note

In Visual Studio 2017 15.5.3, we don’t have in "Code Sharing Strategy" the PCL (Portable Class Library), it’s replaced by .NET Standard.

Xamarin

Creating the domain model and data repository

Create a Models folder in the portable Project.

In the Models folder, create a Contact class that represents our domain model.

  1. public class Contact  
  2. {  
  3.     public string Name { get; set; }  
  4.     public string Email { get; set; }  
  5.     public string Fone { get; set; }  
  6. }  

Create the class ContactRepository in Models folder. This class represents our data repository where we will define the data used in the application.

  1. public class ContactRepository  
  2.     {  
  3.         public IList<Contact> GetContacts { get; private set; }  
  4.   
  5.         public ContactRepository()  
  6.         {  
  7.             GetContacts = new List<Contact> {  
  8.                 new Contact { Name = "Alex Silveira", Email = "egestas@anequeNullam.co.uk", Fone="9985-5623" },  
  9.                 new Contact { Name = "Wilson Martin", Email = "a.tortor@Sed.net",  Fone ="9985-5623" },  
  10.                 new Contact { Name = "Osmar Moss", Email = "tristique@faucibusutnulla.net", Fone ="9985-5623" },  
  11.                 new Contact { Name = "Yasmim Dudley", Email = "montes.nascetur.ridiculus@fringillaest.ca", Fone ="9985-5623" },  
  12.                 new Contact { Name = "Yoshio Anthony", Email = "ut.aliquam.iaculis@pharetraNam.edu" , Fone ="9985-5623"},  
  13.                 new Contact { Name = "Valentina Poole", Email = "auctor@consectetuer.org" , Fone ="9985-5623"},  
  14.                 new Contact { Name = "Armando Tillman", Email = "facilisis.vitae.orci@liberolacusvarius.com" , Fone ="9985-5623"},  
  15.                 new Contact { Name = "Klaus Hickman", Email = "Pellentesque.habitant@tristiqueaceleifend.org" , Fone ="9985-5623" },  
  16.                 new Contact { Name = "Levi Marshall", Email = "imperdiet.ullamcorper@Quisque.com" , Telefone="9985-5623" },  
  17.                 new Contact { Name = "Norberto Boone", Email = "adipiscing@anteipsum.ca" , Fone ="9985-5623" },  
  18.                 new Contact { Name = "Emerlindo Mendez", Email = "aliquet.molestie.tellus@Nam.net" , Fone ="9985-5623" },  
  19.                 new Contact { Name = "Marcos Compton", Email = "Etiam.bibendum.fermentum@malesuadaIntegerid.co.uk" , Fone ="9985-5623" },  
  20.                 new Contact { Name = "Braulio Chapman", Email = "lacinia.orci@aliquetdiamSed.ca" , Fone ="9985-5623" },  
  21.                 new Contact { Name = "Heleno Roberson", Email = "gravida@Nunc.edu" , Fone ="9985-5623" },  
  22.                 new Contact { Name = "Yuri Herrera", Email = "velit@erat.org" , Fone ="9985-5623" },  
  23.                 new Contact { Name = "Lucas Brown", Email = "magnis@Cumsociis.org" , Fone ="9985-5623" },  
  24.                 new Contact { Name = "Gilson Reilly", Email = "vel@NullamenimSed.com" , Fone ="9985-5623" },  
  25.                 new Contact { Name = "Arsenio Suarez", Email = "ridiculus.mus.Aenean@tellusfaucibusleo.co.uk" , Fone ="9985-5623" },  
  26.                 new Contact { Name = "Igor Mclaughlin", Email = "ut.lacus.Nulla@Aliquamnec.edu" , Fone ="9985-3023" },  
  27.                 new Contact { Name = "Carla Craft", Email = "Etiam.gravida.molestie@rutrummagna.ca" , Fone ="9985-3023" },  
  28.                 new Contact { Name = "Benedito Carson", Email = "adipiscing@enimMauris.edu" , Fone ="9985-3023" },  
  29.                 new Contact { Name = "Roberto Reynolds", Email = "commodo@sapienmolestie.edu" , Fone ="9985-3023" },  
  30.                 new Contact { Name = "Denis Webb", Email = "sit.amet.consectetuer@Loremipsumdolor.org" , Fone ="9985-2123" },  
  31.                 new Contact { Name = "Jacob Singleton", Email = "sem.consequat@vehiculaPellentesque.co.uk" , Telefone="9985-2123" },  
  32.                 new Contact { Name = "Carina Tucker", Email = "molestie@erosturpis.ca" , Fone ="9985-2123" },  
  33.                 new Contact { Name = "Felix Holder", Email = "sollicitudin.a@Curae.co.uk" , Fone ="9985-1123" },  
  34.                 new Contact { Name = "Mateus Reid", Email = "Etiam.bibendum@Donecat.edu" , Fone ="9985-1123" },  
  35.                 new Contact { Name = "Anabel Noel", Email = "rhoncus.Donec@vel.edu" , Fone ="9985-4123" },  
  36.                 new Contact { Name = "Karina Dunlap", Email = "lectus@risusQuisque.co.uk" , Fone ="9985-3123" },  
  37.                 new Contact { Name = "Silvio Ewing", Email = "cubilia@afeugiattellus.ca" , Fone ="9985-5123" },  
  38.                 new Contact { Name = "Lucas Reed", Email = "id.risus@Aliquam.edu" , Fone ="9985-4423" },  
  39.                 new Contact { Name = "Geraldo Huff", Email = "non.arcu.Vivamus@fames.edu" , Fone ="9985-0923" },  
  40.                 new Contact { Name = "Fernando Carroll", Email = "ut.nisi.a@elit.edu" , Fone ="9985-0923" },  
  41.                 new Contact { Name = "Leonardo Hamilton", Email = "vitae@penatibusetmagnis.net" , Fone ="9985-0923" },  
  42.                 new Contact { Name = "Myles Knowles", Email = "vitae.aliquam@magna.org" , Fone ="9985-0923" },  
  43.                 new Contact { Name = "Cristina Schmidt", Email = "imperdiet.dictum.magna@vitaeerat.org" , Fone ="9985-0923" },  
  44.                 new Contact { Name = "Thais Ball", Email = "Cras.eu@ataugue.net" , Fone ="9985-0923" },  
  45.                 new Contact { Name = "Renato Mclean", Email = "leo@vitaenibh.net" , Fone ="9985-2323" },  
  46.                 new Contact { Name = "Celio Rogers", Email = "eros.turpis.non@ettristique.co.uk" , Fone ="9985-1023" },  
  47.                 new Contact { Name = "Otavio Estes", Email = "vel@ac.edu" , Fone ="9985-7723"},  
  48.             };  
  49.         }  
  50.     }  
Creating the ViewModel: ContactListViewModel

Create a folder ViewModels in the PCL project and then create the ContactListViewModel class that represents the logic of our View.

  1. public class ContactListViewModel  
  2.     {  
  3.         public IList<Contact> Items { get; private set; }  
  4.         public int ItemsCount { get; private set; }  
  5.         public string MyNumber { get; set; } = "+55 (11) 1111-1111";  
  6.         public ContactListViewModel()  
  7.         {  
  8.             var repo = new ContactRepository();  
  9.             Items = repo.GetContacts.OrderBy(c => c.Name).ToList();  
  10.             ItemsCount = repo.GetContacts.Count;  
  11.         }  
  12.   } 

This code returns the list of contacts in the Items property, the total of contacts in the ItemsCount property, and the phone number defined in MyNumber.

Setting the MainPage Page Code

Open the MainPage.xaml file and include the following code.

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
  4.              xmlns:local="clr-namespace:XF_Agenda"  
  5.              x:Class="XF_Agenda.MainPage"  
  6.              Title="Agenda">  
  7.     <ListView x:Name="LvwContacts"  
  8.         Margin="5"   
  9.         ItemsSource="{Binding Items}"  
  10.         Header="{Binding}"  
  11.         Footer="{Binding}"   
  12.         HasUnevenRows="True">  
  13.   
  14.         <ListView.ItemTemplate>  
  15.             <DataTemplate>  
  16.                 <TextCell  Text="{Binding Name}" Detail="{Binding Email, StringFormat='E-mail: {0}'}"/>  
  17.             </DataTemplate>  
  18.         </ListView.ItemTemplate>  
  19.   
  20.         <ListView.HeaderTemplate>  
  21.             <DataTemplate>  
  22.                 <ContentView BackgroundColor="Beige">  
  23.                     <Label Margin="10" HorizontalOptions="CenterAndExpand" Text="{Binding MeuNumero, StringFormat='My Number : {0}'}" TextColor="Black"/>  
  24.                 </ContentView>  
  25.             </DataTemplate>  
  26.         </ListView.HeaderTemplate>  
  27.   
  28.         <ListView.FooterTemplate>  
  29.             <DataTemplate>  
  30.                 <ContentView BackgroundColor="Aquamarine">  
  31.                     <Label Margin="10" HorizontalOptions="CenterAndExpand" Text="{Binding ItemsCount, StringFormat='Contacts : {0}'}"  TextColor="Black"/>  
  32.                 </ContentView>  
  33.             </DataTemplate>  
  34.         </ListView.FooterTemplate>  
  35.   
  36.     </ListView>  
  37. </ContentPage>  

In the XAML code, we define a ListView where its ItemsSource property is doing the databinding with the Items property of our ViewModel ContactListViewModel.

To display the contacts we are using an ItemTemplate and the TextCell cell,

  • TextCell - Used to display text with the second line as text detail. To do this, simply set the properties:
  • Text - to display the first line of text in larger font;
  • Detail - to display the second line of text in smaller font;
We also define the Header and Footer using the {Binding} notation without defining a Source or Path. XAML understands that the object that will be used for the Binding will be the same as the current BindingContext. Because ListView propagates this object to the Header and Footer templates, this is not necessary.

Next, we set the HeaderTemplate and FooterTemplate settings to display the MyNumber and ItemsCount properties.

Let's now implement the code in the MainPage.xaml.cs file.
  1. using Xamarin.Forms;  
  2. using XF_Agenda.ViewModel;  
  3.   
  4. namespace XF_Agenda  
  5. {  
  6.     public partial class MainPage : ContentPage  
  7.     {  
  8.         public MainPage()  
  9.         {  
  10.             InitializeComponent();  
  11.             BindingContext = new ContactListViewModel();  
  12.         }  
  13.     }  

In this code, we are creating an instance of our ViewModel and assigning it to the BindingContext.

Running the project we will get the following result: (Our default project is Android project)

Xamarin

Now, let's group the information in our Address Book to organize the names by the name’s first letter.

Grouping ListView’s Items

In order to group the information in our Address Book, first, we have to enable our ListView to accept the grouping by setting the following properties.

  1. IsGroupingEnable = True  
  2. GroupDisplayBinding = "{Binding Key}"  

In the MainPage.xaml file,

  1. ...  
  2.    <ListView x:Name="LvwContacts"  
  3.          Margin="5"   
  4.          IsGroupingEnabled="True"        
  5.          GroupDisplayBinding="{Binding Key}"        
  6.          ItemsSource="{Binding Items}"  
  7.          Header="{Binding}"  
  8.          Footer="{Binding}"   
  9.          HasUnevenRows="True">  
  10.  ...  

Now, let´s change the code of our ViewModel ContactListViewModel defining the code to group the information based on the name’s first letter.

  1. public class ContactListViewModel  
  2.     {  
  3.         public IList<Contact> Items { get; private set; }  
  4.         public List<ObservableGroupCollection<string,Contact>> GroupedData { get; set; }  
  5.   
  6.         public int ItemsCount { get; private set; }  
  7.         public string MyNumber { get; set; } = "+55 (11) 1111-1111";  
  8.   
  9.         public ContactListViewModel()  
  10.         {  
  11.             var repo = new ContactRepository();  
  12.             Items = repo.GetContacts.OrderBy(c => c.Name).ToList();  
  13.   
  14.             GroupedData = Items.OrderBy(p => p.Name)  
  15.                .GroupBy(p => p.Name[0].ToString())  
  16.                .Select(p => new ObservableGroupCollection<string, Contact>(p)).ToList();  
  17.   
  18.             ItemsCount = repo.GetContacts.Count;  
  19.         }  
  20.     }  

We define a new property called GroupedData() which is of type List <ObservableGroupCollection<string,Contact>, where we are sorting the names by the first letter of the name.

To support property implementation, we need to create the ObservableGroupCollection class in the ViewModels folder.

  1. public class ObservableGroupCollection<S, T> : ObservableCollection<T>  
  2.     {  
  3.         private readonly S _key;  
  4.         public ObservableGroupCollection(IGrouping<S, T> group)  
  5.             : base(group)  
  6.         {  
  7.             _key = group.Key;  
  8.         }  
  9.         public S Key  
  10.         {  
  11.             get { return _key; }  
  12.         }  
  13.     } 

This class inherits from ObservableCollection allowing you to add or remove items from an existing group, it also sets the first letter to perform grouping.

The Key will be of type string and the collection will be of type IEnumerable <Contact>.

Now, just change the ItemsSource property of the ListView to the new GroupedData property,

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
  4.              xmlns:local="clr-namespace:XF_Agenda"  
  5.              x:Class="XF_Agenda.MainPage"  
  6.              Title="Agenda">  
  7.   
  8.     <ListView x:Name="LvwContacts"  
  9.         Margin="5"   
  10.         IsGroupingEnabled="True"        
  11.         GroupDisplayBinding="{Binding Key}"        
  12.         ItemsSource="{Binding GroupedData}"  
  13.         Header="{Binding}"  
  14.         Footer="{Binding}"   
  15.         HasUnevenRows="True">  
  16.         <ListView.ItemTemplate>  
  17.             <DataTemplate>  
  18.                 <ViewCell>  
  19.                 <StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"  
  20.                      BackgroundColor="Navy">  
  21.                     <Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">  
  22.                         <Label Text="{Binding Name}" FontSize="Small" TextColor="Lime" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand"/>  
  23.                     </Grid>  
  24.                 </StackLayout>  
  25.              </ViewCell>  
  26.             </DataTemplate>  
  27.         </ListView.ItemTemplate>  
  28.         <ListView.HeaderTemplate>  
  29.             <DataTemplate>  
  30.                 <ContentView BackgroundColor="Beige">  
  31.                     <Label Margin="10" HorizontalOptions="CenterAndExpand" Text="{Binding MyNumber, StringFormat='My number : {0}'}" TextColor="Black"/>  
  32.                 </ContentView>  
  33.             </DataTemplate>  
  34.         </ListView.HeaderTemplate>  
  35.         <ListView.FooterTemplate>  
  36.             <DataTemplate>  
  37.                 <ContentView BackgroundColor="Aquamarine">  
  38.                     <Label Margin="10" HorizontalOptions="CenterAndExpand" Text="{Binding ItemsCount, StringFormat='Contacts : {0}'}"  TextColor="Black"/>  
  39.                 </ContentView>  
  40.             </DataTemplate>  
  41.         </ListView.FooterTemplate>  
  42.     </ListView>  
  43. </ContentPage>  
And change the TextCell to a ViewCell with a StackLayout and Label view to show the Name property.

Press F5 to run and build the application.

Xamarin

We now see the Address Book information grouped alphabetically by the first letter of the name.

X

Build smarter apps with Machine Learning, Bots, Cognitive Services - Start free.

Start Learning Now