Xamarin Forms MVVM - IValueConverter
In the previous posts, I have been creating a "calculator" using the MVVM architectural pattern, explaining the ICommand and INotifyPropertyChanged interfaces. I suggest you fork the code from this repository and take a look at the branches so you know what we added in each of those posts, or better yet, read through those posts before continuing with this one.
Because in this post we will take a look at the IValueConverter interface, and how it is a key interface that you can use when you have some information on the Model that has to be presented differently in the View. Say a DateTime that instead of displaying "2018-03-21T15:04:30.0000000-06:00" has to be displayed as "two days ago".
I will implement a very easy example that I hope will make it clear how you can use the interface.
The Status
We already have a "calculator" working with MVVM, the buttons are binding their commands to the ViewModel, and we are binding a result property to the label. What I want to be able to do is, through MVVM, set a TextColor to the result label to green if the result is positive, and to red if it is negative. Normally you would have to evaluate the Result probably every time a button is pressed, and then access the label to set the corresponding color. But that is not an option here, we don't set a name to the label, and we want to be able to easily maintain the code, easily share it, have it in another layer. So what we do is create a class that implements the IValueConverter interface, that receives a double value (the Result) and returns the corresponding color. And then bind that value converter to the label's TextColor to use the value that it already has access to through the Binding Context and receive a color.
The class that implements this interface will be defined inside a new folder, just like the classes that implement the ICommand interface, called Converters, but contrary to those commands, the converter's magic will happen entirely through XAML, without the need for the ViewModel's intervention.
One could argue that the converter is more part of the View than part of the ViewModel, but since it is a way of preparing data to be presented correctly, I will assume it as part of the ViewModel, so if you had a Model, View and ViewModel folders on your project, the Converters folder would go inside the ViewModel folder. Not the case in this example, but just to be clear here.
Let's dive right in.
Implementing the IValueConverter
In this new class, created inside the Converters folder (notice the namespace), I am implementing the IValueConverter interface, which has two members. Two methods that will do the conversion. The Convert method will convert elements from the Model to the View (like the example I just gave about DateTime). But there is also a less popular ConvertBack method. In the cases that you have TwoWay binding, you may need to convert a value from the View back to the Model. This is rarely used because the values in the View that use the converter, seldom allow input from the user (such as a label). I will however implement the functionality so the class is complete, even if that ConvertBack method is never used.
namespace MVVM.Converters { //! added using Xamarin.Forms; public class DoubleToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
The Convert method
Both methods will receive a ton of information to help with the conversion of values. The key value though is precisely the value argument. It is of course of type object so any value can be received, but you will need to do the casting. The parameter is also often useful, in our case we won't use it.
Since I want to return a value that can be set as the color for the TextColor property of a label when the value received is greater or equal than 0, and another color otherwise, the code here is quite simple:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { double result = (double)value; if (result >= 0) return Color.Green; return Color.Red; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return (double)0; }
All that's needed is to cast the object as a double, evaluate it and return the needed color. Notice that my implementation of the ConvertBack, basically so no exception is thrown in case its used, of course, loses all the information, since red/green color won't provide with the previous information. But I can return a 0 so if it is ever needed, the app won't crash due to a NotImplementedException.
Binding the converter
Off to using it on XAML then, like I mention, there's no need for the ViewModel class to intervene. All I need to do is bind the Result property to the TextColor property of the label (in the same way I'm binding it to the Text property) and set a converter so that double results into a value that the TextColor can actually use. Because of course, with no converter, a double as a color makes no sense.
Now to use that converter, I will need a pointer to the namespace of the class (MVVM.Converters in my case). To create that pointer, in the definition of the ContentPage, I can add this line (may need to build the project before seeing some autocomplete options):
xmlns:cvrt="clr-namespace:MVVM.Converters"
With this pointer, we now have access to all the types defined inside that namespace. We can now use it to create a new resource (right below the ViewModel resource, that is the converter. This resource will also need a key:
<cvrt:DoubleToColorConverter x:Key="doubleToColor"/>
This is the resource that we need to set as the Converter for the TextColor property of the label when binding the Result property. The label definition then, will look like this:
<Label Text="{Binding Result}" TextColor="{Binding Result, Converter={StaticResource doubleToColor}}"/>
That's it! The next time you run the project, you will see the label have a color accordingly to the value of the result:
Remember to check the IValueConverter branch in this repo to review the whole code if you need to.
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