Notification about an empty ListView in Xamarin.Forms

demo

ListView is one of my favourite UI controls available in Xamarin.Forms. It is mostly easy to use and customise. Just bind a collection of data, define the representation of each item and you are done!

However, there is one pitfall which most of the developers tend to ignore – if the bound collection is empty, the ListView will have nothing to show. Depending on the targeting platform it may look ugly or confusing for the end user.

In this blogpost we will check few possible solutions. One solution will be purely implemented on the ViewModel level and the other one will be a reusable ListView control wrapper.

Let’s start with a simple case where you have a single ListView in the app and a reusable solution may sound like an overkill. In this case we will need to add an empty list indication to the View/Page and swap the IsVisible property between the ListView and the indicator when the collection become empty:

 <Label
    IsVisible="{Binding IsNotEmptyData, Converter={StaticResource invertBooleanConverter}}"
    VerticalTextAlignment="Center"
    HorizontalTextAlignment="Center"
    FontAttributes="Italic"
    Text="Sorry, it seems that the list is empty." />

<ListView
    IsVisible="{Binding IsNotEmptyData}"
    ItemsSource="{Binding Data}" />

As you see both Label and ListView has the IsVisible property bound to IsNotEmptyData property that is defined on the ViewModel and look like this:

List<string> data;
public List<string> Data
{
    get => data;
    set
    {
        data = value;
        RaisePropertyChanged();
        RaisePropertyChanged(nameof(IsNotEmptyData));
    }
}

public bool IsNotEmptyData => data != null && data.Count > 0;
When Data property is changing it also raising a change event of the IsNotEmptyData property. Quite easy, but not reusable with multiple ListViews in the app.
Second approach I want to demonstrate is a custom control which simply wraps a ListView control by a Label and the visibility logic is managed by it. Here is how our XAML will look like using this approach:
<controls:StaticListViewWithEmptyMessage
    EmptyMessage="Sorry, it seems that the list is empty.">
    <controls:StaticListViewWithEmptyMessage.ListView>
        <ListView ItemsSource="{Binding Data}" />
    </controls:StaticListViewWithEmptyMessage.ListView>
</controls:StaticListViewWithEmptyMessage>
The ListView is still under your full control as it is being defined right here. All you need is to set the EmptyMessage property and you should be ready to go! Lets take a look on the StaticListViewWithEmptyMessage control:
public class StaticListViewWithEmptyMessage : Grid
{
    public ListView ListView
    {
        get { return (ListView)GetValue(ListViewProperty); }
        set { SetValue(ListViewProperty, value); }
    }

    public static readonly BindableProperty ListViewProperty =
        BindableProperty.Create(nameof(ListView), typeof(ListView), typeof(StaticListViewWithEmptyMessage), propertyChanged: HandleListViewChanged);

    static void HandleListViewChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var intelligentListView = (StaticListViewWithEmptyMessage)bindable;
        var oldList = (ListView)oldValue;

        if(oldList != null)
        {
            intelligentListView.Children.Remove(oldList);
            oldList.PropertyChanged -= ListViewPropertyChanged;
        }

        var newList = (ListView)newValue;
        newList.IsVisible = false;
        newList.PropertyChanged += ListViewPropertyChanged;

        intelligentListView.Children.Add(newList);
    }

    static void ListViewPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName != ListView.ItemsSourceProperty.PropertyName) return;

        var listView = (ListView)sender;
        var staticListViewWithEmptyMessage = listView.Parent as StaticListViewWithEmptyMessage;

        if (listView.ItemsSource == null || (listView.ItemsSource as ICollection).Count == 0)
        {
            listView.IsVisible = false;
            staticListViewWithEmptyMessage.emptyMessageLbl.IsVisible = true;
        }
        else
        {
            listView.IsVisible = true;
            staticListViewWithEmptyMessage.emptyMessageLbl.IsVisible = false;
        }
    }

    public string EmptyMessage
    {
        get { return (string)GetValue(EmptyMessageProperty); }
        set { SetValue(EmptyMessageProperty, value); }
    }

    public static readonly BindableProperty EmptyMessageProperty =
        BindableProperty.Create(nameof(EmptyMessage), typeof(string), typeof(StaticListViewWithEmptyMessage));

    readonly Label emptyMessageLbl;

    public StaticListViewWithEmptyMessage()
    {
        Children.Add(emptyMessageLbl = GenerateEmptyLabel());
    }

    Label GenerateEmptyLabel()
    {
        var lbl = new Label
        {
            BindingContext = this, 
            VerticalTextAlignment = TextAlignment.Center,
            HorizontalTextAlignment = TextAlignment.Center,
            FontAttributes = FontAttributes.Italic
        };

        lbl.SetBinding(Label.TextProperty, EmptyMessageProperty.PropertyName);
        return lbl;
    }
}

First we define a BindableProperty of type ListView that allows us to expose the ListView fully as demonstrated above. In order to show/hide our ListView we have to listen and response correctly to related changes.

Please note that both of the examples assumed that the collection bound to the ListView is a static collection if you want to handle a dynamic collection, you will have to use ObservableCollection and listen to CollectionChanged event instead.

The source code is available here.

5 thoughts on “Notification about an empty ListView in Xamarin.Forms

  1. I find it better to use a converter that you pass the list to, that returns true/ false to the isVisable of the list view. Here’s a link to the converter -https://gist.github.com/IeuanWalker/89810279114ae32b17a7e949300da783#file-hasdataconverter-cs

    And then for the Lable, you can use converter above (hasData) with another converter to negate the bool – https://gist.github.com/IeuanWalker/89810279114ae32b17a7e949300da783#file-negatebooleanconverter-cs

    But in order to use multiple converters on one property, you can use the following converter to create a group of converters that can be used as one – https://gist.github.com/IeuanWalker/89810279114ae32b17a7e949300da783#file-valueconvertergroup-cs

    Like

  2. Hi there pardon my ignorance but I was trying to use it this for a dynamic list. How exactly is the CollectionChanged implemented. My ItemSource is already an ObservableCollection?
    How do i implement for dynamic list with CollectionChanged…. ? Thanks for this cool as component, found a place in all or my projects 🙂

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s