Why does ListView manipulate my BackgroundColors when items are selected?

OwenPOwenP USMember ✭✭

I'm working on a ListView that represents some items that we only have partial information for locally. When a user taps an item, I want to display a "busy" indicator as we asynchronously fetch the information. When fetching succeeds, I want to go to the detail page for the item. When it doesn't, I want to display more UI to indicate the failure.

My first idea was to create the busy indicator as an overlay, with a solid background and visibility bound to an IsBusy property. This seems to interact very poorly with whatever Xamarin does when an item is selected. It doesn't seem like there's a way to control this behavior, and it makes me very sad.

(My project doesn't use XAML for reasons that are irrelevant, but here is example code to try it out:)

public class ListViewPage : ContentPage {

    private ListView _listView;

    public ListViewPage() {
        _listView = new ListView();
        _listView.ItemTemplate = new DataTemplate(typeof(ExampleItemCell));

        var items = new ObservableCollection<ExampleViewModel>();
        for (int i = 0; i < 10; i++) {
            items.Add(new ExampleViewModel() { Message = $"Item {i}." });
        }
        _listView.ItemsSource = items;

        _listView.ItemSelected += (sender, e) => {
            var item = _listView.SelectedItem as ExampleViewModel;
            item?.DoWorkCommand?.Execute(null);
        };

        Content = _listView;
    }
}

public class ExampleViewModel : BaseViewModel {

    private string _message;
    public string Message {
        get {
            return _message;
        }
        set {
            SetProperty(ref _message, value);
        }
    }

    private bool _isBusy;
    public bool IsBusy {
        get {
            return _isBusy;
        }
        set {
            SetProperty(ref _isBusy, value);
        }
    }

    private readonly ICommand _doWorkCommand;
    public ICommand DoWorkCommand {
        get {
            return _doWorkCommand;
        }
    }

    public ExampleViewModel() {
        _doWorkCommand = new Command(DoWork);
    }

    private void DoWork() {
        IsBusy = true;

        Task.Delay(5000).ContinueWith((t) => IsBusy = false);
    }
}

public class ExampleItemCell : ViewCell {

    public ExampleItemCell() {

        var layout = new AbsoluteLayout() {
        };

        // This is a standin for an Image in the real project.
        var img = new Label() {
            Text = "X"
        };

        var message = new Label() {
            BackgroundColor = Color.Blue
        };
        message.SetBinding(Label.TextProperty, "Message");

        var indicator = new Label() {
            Text = "Busy!",
            BackgroundColor = Color.White
        };
        indicator.SetBinding(Label.IsVisibleProperty, "IsBusy");

        layout.Children.Add(img, new Rectangle(0, 0, 50, 1), AbsoluteLayoutFlags.HeightProportional);
        layout.Children.Add(message, new Rectangle(50, 0, 1, 1), AbsoluteLayoutFlags.SizeProportional);
        layout.Children.Add(indicator, new Rectangle(0, 0, 1, 1), AbsoluteLayoutFlags.SizeProportional);

        View = layout;

    }

}

What I expect is when I tap an item, the busy indicator appears and covers the item. What happens seems to be, "Every View in the cell is given a transparent background so our selection highlight will show through". This breaks the "overlay" effect and in my real application ruins readability of our fonts.

I see many less complex forms of this problem spreading back to almost the genesis of Xamarin Forms. Some people just don't want a selection rectangle at all. A hack to deal with this problem seems to be to set SelectedItem to null as soon as it's selected. I've tried this in the example above and for some reason I see about 1-2s of time where the effect is still visible. I can't ship with that.

Some people want to control the selection highlight themselves. So far I only see solutions that involve making custom renderers. It feels like this is such a basic request Xamarin should provide some API for it. This goes back to 2014, it's hard to believe such a common request has been answered with "just make a custom renderer for so long".

I'm switching to a slightly different approach where I put an inverted binding on my "normal" UI, but this doesn't feel intuitive at all. In my opinion, XF should not be modifying my Views' background colors at all, and I'm very surprised this is both undocumented and has no mechanism for controlling it.

Am I missing some new feature that makes this behavior extensible?

Sign In or Register to comment.