Disti
Disti

Reputation: 1513

Xamarin Forms ListView: Issues with programmatic selection and custom renderer

I'm having trouble with XF listview and custom renderer. I need to:

I've been able to change background color of selected item and everything is working unless I pre-select an item.

For the custom control and renderers I used the "standard" code found in many examples:

Custom control:

    public class RRViewCell : ViewCell
{
    public static readonly BindableProperty SelectedItemBackgroundColorProperty = BindableProperty.Create("SelectedItemBackgroundColor", typeof(Color), typeof(RRViewCell), Color.Default);

    public Color SelectedItemBackgroundColor
    {
        get { return (Color)GetValue(SelectedItemBackgroundColorProperty); }
        set { SetValue(SelectedItemBackgroundColorProperty, value); }
    }
}

Android renderer:

public class RRViewCellRendererAndroid : ViewCellRenderer
{

    private Android.Views.View _cellCore;
    private Drawable _unselectedBackground;
    private bool _selected;

    protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
    {
        _cellCore = base.GetCellCore(item, convertView, parent, context);

        _selected = false;
        _unselectedBackground = _cellCore.Background;

        return _cellCore;
    }

    protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        base.OnCellPropertyChanged(sender, args);

        if (args.PropertyName == "IsSelected")
        {
            _selected = !_selected;

            if (_selected)
            {
                var extendedViewCell = sender as RRViewCell;
                _cellCore.SetBackgroundColor(extendedViewCell.SelectedItemBackgroundColor.ToAndroid());
            }
            else
            {
                _cellCore.SetBackground(_unselectedBackground);
            }
        }
    }
}

XAML:

<ctrl:RRViewCell SelectedItemBackgroundColor="{StaticResource NavigationPrimary}">

(NavigationPrimary is set to a shade of blue).

As I said, everything is working if no no items are selected programmatically, but if I do:

MyList.SelectedItem = loc[0];

this is the result:

enter image description here

(sensible data masked!)

In this case, defaut background color is used instead of custom one.

Moreover, if I tap another item, this happens:

enter image description here

Please note that:

Upvotes: 0

Views: 317

Answers (2)

Leo Zhu
Leo Zhu

Reputation: 14956

In Android easiest way is edit under Resources\values :

add this line inside styles.xml :

<style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
     ...
   <item name="android:colorActivatedHighlight">@color/ListViewSelected</item>
    ...
</style>

then add the color/ListViewSelected inside colors.xml :

<color name="ListViewSelected">#96BCE3</color>

The downside is that once set, all listviews will enable this effect

Upvotes: 0

ChrisBD
ChrisBD

Reputation: 9209

This can be very frustrating, but with a lot of Xamarin stuff its a case of thinking laterally, but there are many ways of achieving this.

I've achieved something similar by disabling the ListView in built selection process and instead have the selected item always selected programmatically and none of it is platform specific. By doing it this way I can also prevent user selection from having an effect if I am running a long process such as updating the list.

I set the ListView.SelectionMode to None and instead have ListView.ItemTapped trigger my selection code. Each of my ListView source items has an IsSelected property, which is set whenever an item is selected for view. I'm not changing the background colour of the ListView.Item, but instead I use a BoxView with a fixed background colour with it's IsVisible bound to IsSelected e.g.

        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <Grid  RowSpacing="0" >
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="1"/>
                            <ColumnDefinition Width="60"/>
                            <ColumnDefinition Width="120"/>
                            <ColumnDefinition Width="100*"/>
                            <ColumnDefinition Width="40"/>
                            <ColumnDefinition Width="1" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="30*"/>
                        </Grid.RowDefinitions>
                        <BoxView Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="6" BackgroundColor="{StaticResource Primary}" IsVisible="{Binding IsSelected}" />
                        <Label   Grid.Column="1" Grid.Row="0" HorizontalTextAlignment="Center" VerticalOptions="Center" Text="{Binding Destination}"/>
                        <Label   Grid.Column="2" Grid.Row="0" HorizontalTextAlignment="Start" VerticalOptions="Center" Text="{Binding Description}"/>
                        <Label   Grid.Column="3" Grid.Row="0" HorizontalTextAlignment="End"  HorizontalOptions="End" VerticalOptions="Center" Text="{Binding ScaledValue}" FontAttributes="Bold" FontSize="Medium" BackgroundColor="{Binding StoredValueDiffers, Converter={x:StaticResource StoredDiffersConverter}}"/>
                        <Label   Grid.Column="4" Grid.Row="0" HorizontalTextAlignment="Start" VerticalOptions="Center" Text="{Binding Units}"/>
                    </Grid>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>

Because the BoxView is declared first it is in the lowest layer and the other components are drawn over it.

I guess that in your case you could use a non-Boolean value to show whether an item has been selected manually (on tapped), programmatically or not at all and then a converter would select your background colour.

I hope that this gives you some ideas.

Upvotes: 0

Related Questions