In the last few posts, I've been introducing the usage of certain interfaces that help use the MVVM pattern in Xamarin Forms applications. You can even find all that code and identify what I did in each post by switching branches in this repository.
In this post, I'll be talking about the ObservableCollection<T>, which isn't an interface, but does implement the INotifyCollectionChanged interface, which behaves similarly to the INotifyProeprtyChanged interface, and helps integrate notifications for when elements are added, deleted or updated within a collection.
As always, you can find the code in this repo, switch to the ObservableCollection branch to see the changes up to this post and not any further changes I may do.
What we will do
Currently, inside the same "calculator" that we have, we are able to make simple calculations using commands, what I want to add is a list of results, that will, of course, add a new element every time a command is executed. This has to happen through MVVM, so the same ViewModel class that we already have should contain the entire list and update the view when that list changes. This is going to be easily achievable through the use of an ObservableCollection<T>.
In addition to that, this ObservableCollection will have to be somehow bound to a ListView so that the results are displayed. In the case of our example, this will actually be very straight-forward since we will be listing doubles, but I will make some (in this example, useless) changes to explain what you would have to do in case you want to enlist complex types.
Using the class
Since this is already a class, there will no need for us to implement an interface as we did with the other interfaces that we have used. All we need to do is add an ObservableCollaction<double> property to the class. Of course, this will be a collection of doubles since it will be listing the results, which are of type double.
The property will also have to be initialized inside the Constructor for the class, just like it happens with the commands.
//! added using System.Collections.ObjectModel; public ObservableCollection<double> Results { get; set; } // In the constructor: Results = new ObservableCollection<double>();
Of course, this is just the creation, you will also need to add new results as they come in. In the case of this code, it can be done in the setter for the Result property, so everytime a new Result comes in, the ObservableCollection gets updated, now the Result property will look like this:
private double result; public double Result { get { return result; } set { result = value; OnPropertyChanged("Result"); Results.Add(result); } }
What's great about using this class instead of a simple List<T> is that now, every time it changes (aka a new result is added), there will be notifications, similar to the ones we have by calling the OnPropertyChanged method, so the view can be updated accordingly.
Binding to the view
The functionality is there, ready, it is not time to list that collection inside a ListView. Like I mentioned before, in the case of our project, since we are listing doubles, this will be very straight-forward:
<ListView ItemsSource="{Binding Results}"/>
Just like this, we set the source for this ListView to be the ObservableCollection, generating this result:
You may be wondering how are these doubles being displayed as strings if we are never making the cast. Turns out the ListView is smart enough to notice that, and simply calls the ToString method (inherited from the object class to any type) so it can display the value as a string.
In the case of a double, there is no problem here, the ToString method will return that double value as a string without a sweat. But in the case of a complex type, that is not so simple.
Say that instead of an ObservableCollection<double> we had an ObservableCollection<Result>, with result being a class that looks like this:
public class Result { public double Value { get; set; } }
This would of course require a couple of changes in our code.
// In the constructor Results = new ObservableCollection<Result>(); // The Result property private double result; public double Result { get { return result; } set { result = value; OnPropertyChanged("Result"); Results.Add(new Model.Result() { Value = result }); } }
But the main effect will be in the ListView, that will still call the ToString method when trying to display this type, just like it did with the double type, but this time, it won't succeed that gracefully.
For complex types, the ToString method will return the namespace of the type. This is entirely useless. Of course, you may think that you can solve this by simply overriding the ToString method and make it return the result property. You would be correct, a simple override of that method inside the Result class will provide with a solution, a quick one, and in the case of this new example, maybe the optimal.
This version of the ToString method will again return a double as a string, so we could have the previous result when listing these values in the ListView again.
But what happens when you need more than one label to display information? The best way to display the information of a complex type inside a ListView will often be to create a template for each of the items, and just as we bind to the Results ObservableCollection in our example, bind the text of a label that we add inside that template to the Value property of the Result.
We can create a quick example using a default cell style that is already available in Xamarin Forms. The TextCell contains two Labels, I will only use one (the Text label) but the same thing will have to happen if you need to display anything inside the other one (the Detail label).
<ListView ItemsSource="{Binding Results}"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text="{Binding Value}}"/> </DataTemplate> </ListView.ItemTemplate> </ListView>
I am able to bind directly to the Value because for the ItemTemplate, the BindingContext will now be a Result (one of the elements of the ObservableCollection that is the ItemSource for the entire ListView). If you had, for example, an Operation property inside of the Result class that you wanted to display in the second label from the TextCell, you could add 'Detail="{Binding Operation}"' right after setting the TextProperty.
This last part became more of a post on Binding and ListViews, but hopefully you noticed how the ObservableCollection works, as soon as it changes, the View gets updated with new results.
This topic, along with many many others, is covered in greater depth in my 25-hour long "The Complete Xamarin Developer Course: iOS and Android" course, which you can practically steal from me by