Xamarin Forms – Group ListView using an ObservableCollection

As I mentioned in my previous adventure with nested grouped ListView, I first came upon the ListView grouped example using ObservableCollection with the Monkey example. I wasn’t able to figure it out how to alter it to a MVVM version. However, I wanted to know how to use an ObservableCollection, in case I came upon it in the future.

As I had problems with the Monkey repo, I went looking for an easier example to deal with an ObservableCollection. One person noted that there was an ListView grouped example from Xamarin. It looked pretty straightforward, and luckily this example compiled without the dreaded XamlC Task compile error. Unfortunately, this example was like all of the other grouped ListView examples I found and is not a MVVM based example. Since it looked much easier to deal with, I decided to convert it into a MVVM project.

What I did was moved most of the code behind LabelledSectionXaml.xaml.cs source code into the new ViewModel that I created, ViewModelSelection.cs. I also removed the OnItemTapped method in the code behind as part of the conversion to MVVM based example. The example’s XAML:

<ListView x:Name="itemListView"
          IsGroupingEnabled="true" 
          ItemTapped="OnItemTapped"
          GroupDisplayBinding="{Binding LongTitle}"
          GroupShortNameBinding="{Binding Title}"
          Header="HEADER"
          Footer="FOOTER">

In my MVVM conversion, the XAML is now:

<ListView ItemsSource="{Binding PeopleList}"
          SelectedItem="{Binding YourSelectedItem, Mode=TwoWay}"
          GroupDisplayBinding="{Binding LongTitle}"
          IsGroupingEnabled="true" 
          Header="HEADER"
          Footer="FOOTER">

Note: that neither Header or Footer element is needed. I would actually not use either of these as shown.

The PeopleList, YourSelectedItem are properties in the new ViewModel. The GroupShortNameBinding is only shown on iOS, so I removed it.

The loading of the PeopleList property in the ViewModel is pretty much the same as the code behind SetupList() method. This is the most important part of the PopulateList() method:

....
    // If the list group does not exist, we create it.
    if (listItemGroup == null)
    {
        listItemGroup = new ListItemCollection(item.Label);
        listItemGroup.Add(item);
        PeopleList.Add(listItemGroup);
    }
....

This creates a new ListItemCollection using the 1st character of the Name in ListItemValue as the group Key. It adds the collection to the PeopleList. If found, it adds the item to the existing listItemGroup.

The XAML sets the YourSelectedItem property when touched. That keeps track of the value and then shows an alert of what the user selected.

This is the full repo of this example now MVVM-ized.

 

After figuring out the easier ObservableCollection example, it was now time to tackle the harder one: Monkeys. As the Monkey repo has the normal XamlC Task failure, I created v1 of my repo as a copy of his repository which VS Mac will compile. Now onto the hard part – getting this to something that works as other MVVM projects (that I’ve seen anyway).

A few items to note before launching into the relevant MVVM changes:

  1. I removed the choice of the XAML vs. C#, as I only interested in the XAML portion. With this the Views/HomePage.xaml became superfluous as it’s not used in the App.xaml.cs code behind file; I didn’t remove it, so it is still in the repository.
  2. I removed the BindingContext to the static inner class ViewModelLocator – which was in the App.xaml.cs . I’ve not seen this in use anywhere else and was confusing.
  3. I removed the Grouping class that extended the ObservableCollection. This confused things even more on this which is already not obvious. The number one reason for doing this is to create clarity.

I changed the MonkeyHelper from a pseudo-ViewModel back to a true helper, a factory to create a list of Monkeys. I removed the MonkeysGrouped and anything dealing with a ObservableCollection from it.

ViewModel

The MonkeyViewModel didn’t act as a true ViewModel in a MVVM way, so it needed some help. It now extends INotifyPropertyChanged, as all VM should. Added Bind property ObservableCollection<GroupedMonkeys> MonkeysList that will be the basis of the View’s ItemSource. The PopulateList() method is the main driver that populates the MonkeyList ObservableCollection. This is doing what the SetupList() method did for LabelledSectionsList project above. Also added is the SelectedMonkey property that will push to the DetailsPage from the MonkeyPage ListView.

The DetailsViewModel was changed to implement the missing INotifyPropertyChanged.

Views

MonkeyPage.xaml: This was the heart of the changes:

          <ListView ItemsSource="{Binding MonkeysGrouped}"
                    ItemTapped="Handle_ItemTapped"
                    ItemSelected="Handle_ItemSelected"
                    HasUnevenRows="true"
                    GroupShortNameBinding = "{Binding Key}"
                    IsGroupingEnabled = "true"
                    GroupDisplayBinding = "{Binding Key}">

To:

            <ListView ItemsSource="{Binding MonkeysList}"
                      SelectedItem="{Binding SelectedMonkey, Mode=TwoWay}"
                      GroupDisplayBinding = "{Binding Heading}"
                      HasUnevenRows="true"
                      IsGroupingEnabled = "true"

I removed all of the code behind, so the methods dealing with that were removed. I removed the GroupShortNameBinding as that’s not a normal Android feature. The GroupShortNameBinding shows different names; the reason for that was that the Key property went away with the removal of the Grouping class.

The DetailsPage wasn’t substantially changed.

Now, with the v2 change, the Monkeys project is now fully MVVM-ized.

 

These articles really helped me figure out what was going on with grouping ListViews, I do hope that this can help you out as well.

August 26, 2019 Update: You may also want to check out my blog entry on GroupHeaderTemplate vs. GroupDisplayBinding