I know this has been answered quite some time ago, but figured I would add a bit more information here for anybody that may stumble across this question looking for a more MVVM friendly way of doing this. I ended up with the following, which somebody would hopefully find useful if they are so inclined.
You will need a value converter such as the following:
public class UseColorIfConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? parameter : Color.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The converter simply returns the appropriate Color
if the provided parameter evaluates to true. I have also registered this converter as a static resource in my App.xaml (which had to be manually created) i.e:
<?xml version="1.0" encoding="utf-8" ?>
<forms:FormsApplication xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:forms="clr-namespace:Caliburn.Micro.Xamarin.Forms;assembly=Caliburn.Micro.Platform.Xamarin.Forms"
x:Class="Path.To.Application.App"
xmlns:converters="clr-namespace:Path.To.Converters.Namespace;assembly=Converter.Assembly">
<Application.Resources>
<converters:UseColorIfConverter x:Key="UseColorIf"></converters:UseColorIfConverter>
</Application.Resources>
</forms:FormsApplication>
Please note that I am using Caliburn Micro, however the same would work for the default Xamarin.Forms.Application class as well.
The usage of the converter and potential bindings would then be the following:
<ListView ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView BackgroundColor="{Binding Path=Selected, Converter={StaticResource UseColorIf}, ConverterParameter={x:StaticResource ListSelectionColor}}" ...>
<!--Display-->
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This then allows you to bind against a property in each view model which indicates whether it is selected or not. This property has to be kept in sync but is easily enough done by subscribing to the property changed events for example:
public class MenuViewModel : Screen
{
public BindableCollection<SectionViewModel> Items { get; }
public MenuViewModel(IEnumerable<SectionViewModel> sections)
{
Items = new BindableCollection<SectionViewModel>(sections);
PropertyChanged += OnPropertyChanged;
}
private SectionViewModel _selectedItem;
public SectionViewModel SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem == value)
return;
_selectedItem = value;
NotifyOfPropertyChange(nameof(SelectedItem));
}
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
if (propertyChangedEventArgs.PropertyName == nameof(SelectedItem))
{
foreach (var item in Items)
{
item.Selected = item == SelectedItem;
}
}
}
}
There is also one niggling thing remaining after doing this and that is the default renderer used on each platform. I haven't checked Android, but at least on IOS you will still get a gray border so the following just removes it:
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ViewCell), typeof(CustomListViewCellRenderer))]
namespace My.Awesome.Ios.Client.Renderers
{
class CustomListViewCellRenderer : ViewCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var cell = base.GetCell(item, reusableCell, tv);
cell.SelectionStyle = UITableViewCellSelectionStyle.None;
return cell;
}
}
}