Xamarin Forms Custom Listview with some added awesomeness… ;)

Let me make it obvious, are you looking for a Xamarin Forms Listview that could have awesome features such as Infinite Scrolling, Load More footer button, Activity Indicator, Data Source awareness, and so on ? Alrighto ! then keep on reading my friend… 😉

It’s about to go down! 😛

So there was this one day, I came across implementing a simple Listview with an infinite data loading, whereas when the user reaches the end of the listview scroll, it should automatically load more items to the listview and so on. And easy enough I recalled some article I read through Xamarin Weekly Newsletter sometime back, so my lazy self wanted to look it up and check out the implementation. http://www.codenutz.com/lac09-xamarin-forms-infinite-scrolling-listview/ and yes it was a great tutorial indeed, I ended up finishing the implementation in few hours.
But my boss man wanted me to add a “Load More items” button at the bottom of the listview, instead of having to load items automatically while scrolling.

Well well, it seemed like no more lazing around, and just to make it fun, I took this as an opportunity to create an awesome-Listview with both those capabilities included and few more added sweetness… 😉 So here I am sharing my code as usual… 😀

And yes I must admit that I got inspired by the www.codenutz.com infinite scroll listview and I used his implementation of infinite loading method code in my own improved listview, so a big shout out for the developer of that listview. 🙂 You can check his github from here. Codenutz.XF.InfiniteListView

behold the AwesomeListView …

Now I shall reveal the Awesome-ListView of mine… 😀 and yes of course I literally named it the AwesomeListView ! lol

Instead of chunks of the code, I shall post the entire code at once, so anyone could easily use it upon their wish.. 😉

namespace WhateverYourNamespace.Controls
{
    /// <summary>
    /// A simple listview that exposes a bindable command to allow infinite loading behaviour.
    /// Along with Footer 'Load more' items button functionality and Auto Disable functionality
    /// </summary>
    public class AwesomeListView : ListView
    {
        Button btn_LoadMoreItems;

        ActivityIndicator ActivityIndicator_LoadingContacts;

        /// <summary>
        /// Constructor - Pass in true/false to visible/hide Listview Footer 'Load more' items button
        /// </summary>
        /// <param name="IsLoadMoreFooterVisible"></param>
        public AwesomeListView(bool IsLoadMoreItemsFooterVisible = false)
        {
            ActivityIndicator_LoadingContacts = new ActivityIndicator
            {
                Color = Color.FromHex("#FF3300"),
                IsRunning = true,
                HorizontalOptions = LayoutOptions.FillAndExpand,
            };
            
            if (IsLoadMoreItemsFooterVisible)
            {
                // Setting up 'Load More' items footer

                btn_LoadMoreItems = new Button
                {
                    Text = "Load More... ",
                    TextColor = Color.White,
                    BackgroundColor = Color.FromHex("#0066FF"),
                    BorderRadius = 0,
                    BorderWidth = 0,
                    BorderColor = Color.FromHex("#0066FF"),
                    VerticalOptions = LayoutOptions.EndAndExpand,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                };
                btn_LoadMoreItems.Clicked += btn_LoadMoreItems_Clicked;

                this.IsLoadMoreItemsPossible = true;

                this.Footer = new StackLayout
                {
                    Orientation = StackOrientation.Vertical,
                    Children = { ActivityIndicator_LoadingContacts, btn_LoadMoreItems },
                    Padding = new Thickness(0,5,0,5)
                };
            }
            else
            {
                // Setting up 'Infinite Scrolling' items

                ItemAppearing += AwesomeListView_ItemAppearing;

                this.Footer = new StackLayout
                {
                    Orientation = StackOrientation.Vertical,
                    Children = { ActivityIndicator_LoadingContacts },
                    Padding = new Thickness(0, 5, 0, 5)
                };
            }
        }

        #region UI Control Events

        /// <summary>
        /// Click event of Listview Footer 'Load More' items button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_LoadMoreItems_Clicked(object sender, EventArgs e)
        {
            if (LoadItemsMoreFooterCommand != null && LoadItemsMoreFooterCommand.CanExecute(null))
                LoadItemsMoreFooterCommand.Execute(null);
        }

        /// <summary>
        /// List View ItemAppearing event for infinite scrolling
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AwesomeListView_ItemAppearing(object sender, ItemVisibilityEventArgs e)
        {
            // Checking whether its possible to load more items
            if (!IsLoadMoreItemsPossible)
            {
                return;
            }

            var items = ItemsSource as IList;

            if (items != null && e.Item == items[items.Count - 1])
            {
                if (LoadMoreInfiniteScrollCommand != null && LoadMoreInfiniteScrollCommand.CanExecute(null))
                    LoadMoreInfiniteScrollCommand.Execute(null);
            }
        }

        #endregion

        #region Bindable Properties

        /// <summary>
        /// Gets or sets the property binding that defines whether the list has reached the 
        /// end of the items source thereby declaring its not possible to load more itmes... 
        /// </summary>
        public static BindableProperty IsLoadMoreItemsPossibleProperty = BindableProperty.Create<AwesomeListView, bool>(ctrl => ctrl.IsLoadMoreItemsPossible,
        defaultValue: false,
        defaultBindingMode: BindingMode.TwoWay,
        propertyChanging: (bindable, oldValue, newValue) =>
        {
            var ctrl = (AwesomeListView)bindable;
            ctrl.IsLoadMoreItemsPossible = newValue;
        });

        /// <summary>
        /// Gets or sets the property binding that defines whether the listview is busy 
        /// loading items in the background to the UI
        /// </summary>
        public static BindableProperty IsBusyProperty = BindableProperty.Create<AwesomeListView, bool>(ctrl => ctrl.IsBusy,
        defaultValue: false,
        defaultBindingMode: BindingMode.TwoWay,
        propertyChanging: (bindable, oldValue, newValue) =>
        {
            var ctrl = (AwesomeListView)bindable;
            ctrl.IsBusy = newValue;
        });

        /// <summary>
        /// Respresents the command that is fired to ask the view model to load additional data bound collection.
        /// </summary>
        public static readonly BindableProperty LoadMoreInfiniteScrollCommandProperty = BindableProperty.Create<AwesomeListView, ICommand>(bp => bp.LoadMoreInfiniteScrollCommand, default(ICommand));

        /// <summary>
        /// Respresents the command that is fired to ask the view model to load additional data bound collection.
        /// </summary>
        public static readonly BindableProperty LoadMoreItemsFooterCommandProperty = BindableProperty.Create<AwesomeListView, ICommand>(bp => bp.LoadItemsMoreFooterCommand, default(ICommand));

        #endregion

        #region Propertries

        /// <summary>
        /// Gets or sets the property value that defines whether the list has reached the 
        /// end of the items source thereby declaring its not possible to load more itmes...
        /// </summary>
        public bool IsLoadMoreItemsPossible
        {
            get { return (bool)GetValue(IsLoadMoreItemsPossibleProperty); }
            set
            {
                SetValue(IsLoadMoreItemsPossibleProperty, value);

                // Setting the value to button if it is available
                if (btn_LoadMoreItems != null)
                {
                    btn_LoadMoreItems.IsEnabled = value;

                    // Setting grey color for the button background in disabled state
                    if (!btn_LoadMoreItems.IsEnabled)
                    {
                        if (Device.OS == TargetPlatform.Android || Device.OS == TargetPlatform.iOS)
                        {
                            btn_LoadMoreItems.BackgroundColor = Color.FromHex("#C0C0C0");
                            btn_LoadMoreItems.BackgroundColor = Color.FromHex("#C0C0C0");
                        }
                    }
                    else
                    {
                        if (Device.OS == TargetPlatform.Android || Device.OS == TargetPlatform.iOS)
                        {
                            btn_LoadMoreItems.BackgroundColor = Color.FromHex("#0066FF");
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets the property value that defines whether listview is busy loading
        /// items in the background or not
        /// </summary>
        public bool IsBusy 
        {
            get { return (bool)GetValue(IsBusyProperty); }
            set
            {
                SetValue(IsBusyProperty, value);

                // Setting the value to ActivityIndicator
                if (ActivityIndicator_LoadingContacts != null)
                {
                    ActivityIndicator_LoadingContacts.IsVisible = value;
                }
            }
        }

        #endregion

        #region Commands

        /// <summary>
        /// <summary>
        /// Gets or sets the command binding that is called whenever the listview is reaching the bottom items area
        /// </summary>
        public ICommand LoadMoreInfiniteScrollCommand
        {
            get { return (ICommand)GetValue(LoadMoreInfiniteScrollCommandProperty); }
            set { SetValue(LoadMoreInfiniteScrollCommandProperty, value); }
        }

        /// <summary>
        /// <summary>
        /// Gets or sets the command binding that is called whenever user clicks on the 'Load More' footer button in the ListView
        /// </summary>
        public ICommand LoadItemsMoreFooterCommand
        {
            get { return (ICommand)GetValue(LoadMoreItemsFooterCommandProperty); }
            set { SetValue(LoadMoreItemsFooterCommandProperty, value); }
        }

        #endregion
    }
}

 

Alright, I shall discuss some of it’s added awesomeness from one after the other…

Constructor – As you can see above in our  AwesomeListView’s constructor I have defined a boolean parameter, which defines whether you need to use the ListView with the infinite scrolling enabled ? or whether you want to use the ListView with the Load More footer button enabled. This is very easy to use based up on your choice… 😉 When you pass in true it will enable the button and add it to the footer of the listview, along with its button clicks and commands. If not it will use the infinite scrolling feature.

UI Control Events – There are two event handlers, btn_LoadMoreItems_Clicked is for handling the click event of the footer button and the next one AwesomeListView_ItemAppearing is for handling the infinite scrolling. Inside that we have the logic for checking the appearance of the last item in the listview, and based on that the Load more command is fired. Also you may have noticed that it’s checking for a boolean value inside the event, which is to check whether the datasource has the capability to provide more items for the ListView to display. This can also be externally wired up by the ViewModel, and it’s very useful because the ListView won’t be wasting anytime firing up unnecessary data loading calls to the viewmodel when the datasource has no more data to provide.

Bindable Properties – We will be using those Bindable Propoerties to wire up the necessary functionality of this ListView to the outside code. IsLoadMoreItemsPossibleProperty defines whether it’s possible to load more data to the listview or not, based on the values provided by the ViewModel and IsBusyProperty is for defining whether the ListView is busy loading data in the background. And then comes the LoadMoreInfiniteScrollCommandProperty and LoadMoreItemsFooterCommandProperty, which are the Command Properties you could use for binding the ListView commands whether you need to call them based on infinite scroll or from the footer button command. You need to use the command property upon your  choice whether you need the infinite scroll listview or the footer button enabled listview. 😉

Propertries – I have defined a IsLoadMoreItemsPossible property which defines whether its possible to load more items from the data source in the viewmodel. This could be wired up through the above explained bindable property. There you may have noticed that in the setter method I’m setting the value directly to the footer button’s enable property, so that when the ViewModel informs the listview there are no more items to load in the data source, then the Footer load more items button will be disabled automatically. 😀 Then we have the IsBusy property which denotes whether the ListView is busy (actually the ViewModel) loading data from the background service or from the datasource to the UI. This is also used to make visible or hide the ActivityIndicator based on the passed in value of the ViewModel.

CommandsLoadMoreInfiniteScrollCommand and LoadItemsMoreFooterCommand, These are the Commands that will be used for loading data based on whether you have chosen the infinite scroll listview or the footer button enabled listview. These commands will be communicating through the bindable properties I have described above.

How to ?

Here is how you could use this in your own application if you ever find it confusing to implement this AwesomeListView… 😉

// Pass in the parameter defining whether you need the Infinite scroll or load more footer enabled
AwesomeListView ListView_Contacts = new AwesomeListView(true);

// Whatever your item template
var ListItemTemplate = new DataTemplate(typeof(TextCell));
ListItemTemplate.SetBinding(TextCell.TextProperty, "Name");
ListItemTemplate.SetBinding(TextCell.DetailProperty, "Phone");

// Setting the bindings for the AwesomeListView from your ViewModel as you please..
ListView_Contacts.SetBinding(AwesomeListView.ItemsSourceProperty, "ContactsList");
ListView_Contacts.SetBinding(AwesomeListView.SelectedItemProperty, "SelectedContact");
ListView_Contacts.SetBinding(AwesomeListView.IsEnabledProperty, "IsFree");
ListView_Contacts.SetBinding(AwesomeListView.IsBusyProperty, "IsBusy");
ListView_Contacts.SetBinding(AwesomeListView.IsLoadMoreItemsPossibleProperty, "IsLoadMoreEnabled");
ListView_Contacts.ItemTemplate = ListItemTemplate;

ListView_Contacts.SetBinding(AwesomeListView.LoadMoreItemsFooterCommandProperty, "LoadMoreContactsCommand");

 

Now how about you wanted to use the infinite scrolling functionality in our AwesomeListView ? It’s quite simple enough…

// Pass in the parameter defining whether you need the Infinite scroll or load more footer enabled
AwesomeListView ListView_Contacts = new AwesomeListView(false);

// Setting the bindings for the AwesomeListView from your ViewModel as you please..
ListView_Contacts.SetBinding(AwesomeListView.LoadMoreInfiniteScrollCommandProperty, "LoadMoreContactsCommand");

 

Change the constructor parameter to false and wire up the LoadMoreInfiniteScrollCommandProperty to your ViewModel. 😀

Here’s some screenshots of our AwesomeListView in action…

Untitled design

Anyhow I shall write another complete article along with a full example implementation of my AwesomeListView according to the MVVM pattern. Stay tuned fellas ! 😀

Well there it goes folks, hope this post was helpful for you and prolly saved you a lot of time if you were ever intrigued by the same situation I went through… 🙂 Please share this among other developers and you may save another developer’s time as well… 😉

Cheers ! Stay Awesome ^_^ !

Advertisements

One thought on “Xamarin Forms Custom Listview with some added awesomeness… ;)

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s