I love Converters in Xamarin Forms. They give you a nice, easy way to display something through your XAML based on a value in your binding data. And they are so easy to set up.
All converters inherit from IValueConverter and must implement two methods: Convert and ConvertBack.
public class MyConverter : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
// do something to get a new value | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
// and change it back if you want to | |
} | |
} |
Then, to use it, in your XAML, add your reference to the converter in either your App.xaml (if you expect to use it a lot) or directly on the page you wish to use it.
<?xml version="1.0" encoding="utf-8" ?> | |
<Application xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
xmlns:converter="clr-namespace:MyApp.Converter;assembly=MyApp" | |
x:Class="MyApp.App"> | |
<Application.Resources> | |
<ResourceDictionary> | |
<converter:ItemTappedEventArgsConverter x:Key="ItemTappedEventArgsConverter" /> | |
<converter:FirstValidationErrorConverter x:Key="FirstValidationErrorConverter" /> | |
<converter:BooleanNegationConverter x:Key="Inverter" /> | |
</ResourceDictionary> | |
</Application.Resources> | |
</Application> |
I think the easiest way to see this in action is to run through some of the converters I’ve used. Most of these are probably common to a lot of Xamarin developers, but I’m going to add them in any way.
Boolean Negation Converter
This lets you convert a true to false, and false to true (obviously!)
public class BooleanNegationConverter : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
return !(bool)value; | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
return !(bool)value; | |
} | |
} |
First Validation Error Converter
This is a converter I came across while learning Xamarin Forms, from a Microsoft example project. It allows you to display the first error set against a bound value that is a ValidatableObject. I’d recommend looking at the eShopOnContainers sample code if you’re interested in learning more about this.
public class FirstValidationErrorConverter : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
ICollection<string> errors = value as ICollection<string>; | |
return errors != null && errors.Count > 0 ? errors.ElementAt(0) : null; | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} |
Item Tapped Event Args Converter
This is another I came across when first learning Xamarin Forms and I still use it quite a bit.
public class ItemTappedEventArgsConverter : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
var eventArgs = value as ItemTappedEventArgs; | |
if (eventArgs == null) | |
throw new ArgumentException("Expected TappedEventArgs as value", "value"); | |
return eventArgs.Item; | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} |
This allows you to capture an action against a control and bind it to a command. It needs to be used in conjunction with a Behavior, but it’s a very useful combo.
public class EventToCommandBehavior : BindableBehavior<Xamarin.Forms.View> | |
{ | |
public static BindableProperty CommandProperty = | |
BindableProperty.CreateAttached( | |
"Command", | |
typeof(ICommand), | |
typeof(EventToCommandBehavior), | |
null, | |
BindingMode.OneWay); | |
public static BindableProperty CommandParameterProperty = | |
BindableProperty.CreateAttached( | |
"CommandParameter", | |
typeof(object), | |
typeof(EventToCommandBehavior), | |
null, | |
BindingMode.OneWay); | |
public static BindableProperty EventArgsConverterProperty = | |
BindableProperty.CreateAttached( | |
"EventArgsConverter", | |
typeof(IValueConverter), | |
typeof(EventToCommandBehavior), | |
null, | |
BindingMode.OneWay); | |
public static BindableProperty EventArgsConverterParameterProperty = | |
BindableProperty.CreateAttached( | |
"EventArgsConverterParameter", | |
typeof(object), | |
typeof(EventToCommandBehavior), | |
null, | |
BindingMode.OneWay); | |
protected Delegate _handler; | |
private static BindableProperty EventNameProperty = | |
BindableProperty.CreateAttached( | |
"EventName", | |
typeof(string), | |
typeof(EventToCommandBehavior), | |
null, | |
BindingMode.OneWay); | |
private EventInfo _eventInfo; | |
public string EventName | |
{ | |
get { return (string)GetValue(EventNameProperty); } | |
set { SetValue(EventNameProperty, value); } | |
} | |
public ICommand Command | |
{ | |
get { return (ICommand)GetValue(CommandProperty); } | |
set { SetValue(CommandProperty, value); } | |
} | |
public object CommandParameter | |
{ | |
get { return GetValue(CommandParameterProperty); } | |
set { SetValue(CommandParameterProperty, value); } | |
} | |
public IValueConverter EventArgsConverter | |
{ | |
get { return (IValueConverter)GetValue(EventArgsConverterProperty); } | |
set { SetValue(EventArgsConverterProperty, value); } | |
} | |
public object EventArgsConverterParameter | |
{ | |
get { return GetValue(EventArgsConverterParameterProperty); } | |
set { SetValue(EventArgsConverterParameterProperty, value); } | |
} | |
protected override void OnAttachedTo(Xamarin.Forms.View visualElement) | |
{ | |
base.OnAttachedTo(visualElement); | |
var events = AssociatedObject.GetType().GetRuntimeEvents().ToArray(); | |
if (events.Any()) | |
{ | |
this._eventInfo = events.FirstOrDefault(e => e.Name == EventName); | |
if (this._eventInfo == null) | |
throw new ArgumentException(String.Format("EventToCommand: Can't find any event named '{0}' on attached type", EventName)); | |
AddEventHandler(this._eventInfo, this.AssociatedObject, this.OnFired); | |
} | |
} | |
protected override void OnDetachingFrom(Xamarin.Forms.View view) | |
{ | |
if (this._handler != null) | |
{ | |
this._eventInfo.RemoveEventHandler(this.AssociatedObject, this._handler); | |
} | |
base.OnDetachingFrom(view); | |
} | |
private void AddEventHandler(EventInfo eventInfo, object item, Action<object, EventArgs> action) | |
{ | |
var eventParameters = eventInfo.EventHandlerType | |
.GetRuntimeMethods().First(m => m.Name == "Invoke") | |
.GetParameters() | |
.Select(p => Expression.Parameter(p.ParameterType)) | |
.ToArray(); | |
var actionInvoke = action.GetType() | |
.GetRuntimeMethods().First(m => m.Name == "Invoke"); | |
this._handler = Expression.Lambda( | |
eventInfo.EventHandlerType, | |
Expression.Call(Expression.Constant(action), actionInvoke, eventParameters[0], eventParameters[1]), | |
eventParameters) | |
.Compile(); | |
eventInfo.AddEventHandler(item, this._handler); | |
} | |
private void OnFired(object sender, EventArgs eventArgs) | |
{ | |
if (this.Command == null) | |
{ | |
return; | |
} | |
var parameter = this.CommandParameter; | |
if (eventArgs != null && eventArgs != EventArgs.Empty) | |
{ | |
parameter = eventArgs; | |
if (this.EventArgsConverter != null) | |
{ | |
parameter = this.EventArgsConverter.Convert(eventArgs, typeof(object), this.EventArgsConverterParameter, CultureInfo.CurrentUICulture); | |
} | |
} | |
if (this.Command.CanExecute(parameter)) | |
{ | |
this.Command.Execute(parameter); | |
} | |
} | |
} |
For example, I can bind the tapping of a ListView item to my code in my View Model by doing:-
<ListView ItemsSource="{Binding ListData}" HasUnevenRows="True"> | |
<ListView.Behaviors> | |
<behaviors:EventToCommandBehavior EventName="ItemTapped" Command="{Binding ListItemSelected}" EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" /> | |
</ListView.Behaviors> | |
<ListView.ItemTemplate> | |
<DataTemplate> | |
<ViewCell> | |
<!- your layout here–> | |
</ViewCell> | |
</DataTemplate> | |
</ListView.ItemTemplate> | |
</List> |
String Has Value Converter
This was one I used to show a message if one had been supplied when a busy indicator was being displayed.
public class StringHasValueConverter : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
if (value == null) return false; | |
return !string.IsNullOrEmpty(value.ToString()); | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} |
<?xml version="1.0" encoding="utf-8" ?> | |
<ResourceDictionary | |
x:Class="MyApp.Styles.Indicator" | |
xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> | |
<ControlTemplate x:Key="MainTemplate"> | |
<Grid BindingContext="{TemplateBinding BindingContext}"> | |
<ContentPresenter Grid.Row="0" /> | |
<!– 'Busy' indicator –> | |
<StackLayout | |
Grid.Row="0" | |
BackgroundColor="{DynamicResource BackgroundIndicatorColor}" | |
HorizontalOptions="FillAndExpand" | |
IsVisible="{TemplateBinding BindingContext.IsBusy}" | |
VerticalOptions="FillAndExpand"> | |
<StackLayout | |
HorizontalOptions="CenterAndExpand" | |
VerticalOptions="CenterAndExpand"> | |
<Frame | |
Margin="10" | |
Padding="10" | |
CornerRadius="40" | |
HeightRequest="100" | |
WidthRequest="200"> | |
<StackLayout> | |
<ActivityIndicator | |
BackgroundColor="Transparent" | |
HeightRequest="48" | |
HorizontalOptions="Center" | |
IsRunning="{TemplateBinding BindingContext.IsBusy}" | |
IsVisible="{TemplateBinding BindingContext.IsBusy}" | |
VerticalOptions="Center" | |
WidthRequest="48" | |
Color="{DynamicResource IndicatorColor}" /> | |
<Label | |
IsVisible="{TemplateBinding BindingContext.IsBusyText, | |
Converter={StaticResource StringHasValueConverter}}" | |
HorizontalOptions="Center" | |
HorizontalTextAlignment="Center" | |
Style="{DynamicResource TextPrimaryColor}" | |
Text="{TemplateBinding BindingContext.IsBusyText}" /> | |
</StackLayout> | |
</Frame> | |
</StackLayout> | |
</StackLayout> | |
</Grid> | |
</ControlTemplate> | |
</ResourceDictionary> |
Alternate Row Colour Converter
Set alternating colours on a Listview.
public class AlternateRowColourConverter : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
if (value == null || parameter == null) return Color.LightGray; | |
var index = ((ListView)parameter).ItemsSource.Cast<object>().ToList().IndexOf(value); | |
if (index % 2 == 0) | |
{ | |
return "#eaecef"; | |
} | |
else | |
{ | |
return "#d7d9dd"; | |
} | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} |
<ListView> | |
<ListView.ItemTemplate> | |
<DataTemplate> | |
<ViewCell> | |
<Grid | |
Padding="5" | |
BackgroundColor="{Binding ., Converter={StaticResource AlternateRowColourConverter}, ConverterParameter={x:Reference ItemsToFit}}" | |
HorizontalOptions="FillAndExpand"> | |
<!– your stuff here –> | |
</Grid> | |
</ViewCell> | |
</DataTemplate> | |
</ListView.ItemTemplate> | |
</ListView> |
Direction Icon Converter
This was used to display a direction icon, utilising the Material Design Icons as per James Montemagno’s post, making it really easy to show an icon against each row.
public class DirectionIconConverter : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
var direction = value.ToString().ToLower(); | |
switch (direction) | |
{ | |
case "north": | |
return IconConstants.ArrowUpBoldCircle; | |
case "south": | |
return IconConstants.ArrowDownBoldCircle; | |
case "east": | |
return IconConstants.ArrowRightBoldCircle; | |
case "west": | |
return IconConstants.ArrowLeftBoldCircle; | |
default: | |
return IconConstants.HelpCircle; | |
} | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} |

Conclusion
Hopefully, this has shown just how useful and easy converters are to use, as well as some ideas on how you could use them in your apps.
I noticed that most examples don’t implement ConvertBack and leave it with a
throw new NotImplementedException();Do you know why?Per the MS documentation, we should either do as your first example and return the same value, and in any case where there are conversions made to the data, since this is the way back from Target to Source we should un-convert, like this:
public class IntToBoolConverter : IValueConverter { public object Convert (objectvalue, Type targetType, object parameter, CultureInfo culture) { return (int)value != 0; } public object ConvertBack (objectvalue, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? 1 : 0; } }
LikeLike
Hey alejandrormzI hope you see this comment – for some reason, it appears this blog site will *not* allow me to reply to comments. (I won’t be staying with Wix when my year is up…)
Anyway – if you do see this you are right. I’ve only the one converter with the ConvertBack method implemented. In most cases, I’m only bothered about one-way conversions and I’ve seen a lot of similar implementations. I would, however, recommend having a look at the converters in the Xamarin Community Toolkit https://github.com/xamarin/XamarinCommunityToolkit/tree/main/XamarinCommunityToolkit/Converters
LikeLike