I did come up with a solution which I am happy with and requires no Renderer. In my custom ViewCell I have added the SelectedBackgroundColor BindableProperty as suggested.
public static readonly BindableProperty SelectedBackgroundColorProperty =
BindableProperty.Create("SelectedBackgroundColor", typeof(Color), typeof(SymbolViewCell), Color.Transparent, propertyChanged:SelectionColorChanged);
public Color SelectedBackgroundColor
{
get => (Color)GetValue(SelectedBackgroundColorProperty);
set => SetValue(SelectedBackgroundColorProperty, value);
}
private static void SelectionColorChanged(BindableObject bindable, object oldvalue, object newvalue)
{
if ( !(bindable is SymbolViewCell viewCell) ) return;
var color = (Color) newvalue;
viewCell.View.BackgroundColor = color;
}
I then use a custom Converter. This is actually a generic Converter that is used elsewhere for setting values based on a true/false bound value.
public class ConfigurableBoolConverter<T> : IValueConverter
{
public ConfigurableBoolConverter() { }
public ConfigurableBoolConverter(T trueResult, T falseResult)
{
TrueResult = trueResult;
FalseResult = falseResult;
}
public T TrueResult { get; set; }
public T FalseResult { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (TrueResult == null || FalseResult == null) return !(bool)value;
return value is bool b && b ? TrueResult : FalseResult;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (TrueResult == null || FalseResult == null) return !(bool)value;
return value is T variable && EqualityComparer<T>.Default.Equals(variable, TrueResult);
}
}
In Xaml I define the Converter and set my True/False values to the required background colors:
<converters:ConfigurableBoolConverter x:Key="BackgroundColorConverter"
x:TypeArguments="x:String"
TrueResult="Color.LightGray"
FalseResult="Color.Transparent"/>
Then assign the Converter to the custom ViewCell. In the custom ViewCell the SelectedBackgroundColor was set using the Converter. As a note The SymbolViewCell already existed to solve a different issue with an image that was part of the item refreshing correctly
<DataTemplate>
<views:SymbolViewCell
SelectedBackgroundColor="{Binding IsChecked, Converter={StaticResource
BackgroundColorConverter}}"/>
</DataTemplate>
IsChecked is a property on the Item of the ItemsDataSource. The ListView already used a a collection of Item objects and this object already had an IsChecked property.
Stripping down the Item object to the bare minimum (BindableBase implements the IPropertyChanged interface):
public class SymbolItem : BindableBase
{
private bool? _isChecked;
public SymbolItem(LegendInfo legendInfo, FeatureTemplate featureTemplate, ArcGISFeatureTable featureTable, IEnumerable<string> requiredFields)
{
IsChecked = false;
}
public bool? IsChecked
{
get => _isChecked;
set => SetProperty(ref _isChecked, value);
}
}
This solution would not work if the ItemsDataSource was a collection of string objects because it does require the additional property and you would need a bound SelectedItem property as a place to trigger the change in IsChecked property. But one could make a simple object with a name and IsChecked property to bind. Personally I think this added code is a lot simpler than writing a Renderer to handle things.
public SymbolItem SelectedSymbolItem
{
get => _selectedSymbolItem;
set
{
if ( _selectedSymbolItem != null ) _selectedSymbolItem.IsChecked = false;
SetProperty(ref _selectedSymbolItem, value);
}
}