Infinite Scrolling ListView For Xamarin.Forms App Using Behaviors

Behaviors is one of the extremely useful features in Xamarin.Forms that allows the user to add a new functionality in an already existing element in the view. Programmers just have to create a class that inherits from Behavior<T> where ‘T’ is an Element which is being extended. ListView is a control to store list of objects whose source might be static data from the web. But once in a while programmers need to get limited data time as a user scrolls (For example for news, a user might need to get 10 pieces of news at a time as news in the server may be  in the thousands in number causing the app to crash). As Infinite scrolling is not a feature in ListView we have to insert a piece of code to get this functionality.

Create a new Xamarin.Forms App and select PCL(Portable Class Library),

Xamarin

Create a Folder named “Behaviors” and add a class InfiniteScroll inside it,

Xamarin

Inherit the class Behavior<ListView> which lies in namespace Xamarin.Forms and add the following codes,

  1. using System;    
  2. using System.Collections;    
  3. using System.Windows.Input;    
  4. using Xamarin.Forms;    
  5. namespace InfiniteScrollApp.Behaviors {    
  6.     public class InfiniteScroll: Behavior < ListView > {    
  7.         public static readonly BindableProperty LoadMoreCommandProperty = BindableProperty.Create("LoadMoreCommand"typeof(ICommand), typeof(InfiniteScroll), null);    
  8.         public ICommand LoadMoreCommand {    
  9.             get {    
  10.                 return (ICommand) GetValue(LoadMoreCommandProperty);    
  11.             }    
  12.             set {    
  13.                 SetValue(LoadMoreCommandProperty, value);    
  14.             }    
  15.         }    
  16.         public ListView AssociatedObject {    
  17.             get;    
  18.             private set;    
  19.         }    
  20.         protected override void OnAttachedTo(ListView bindable) {    
  21.             base.OnAttachedTo(bindable);    
  22.             AssociatedObject = bindable;    
  23.             bindable.BindingContextChanged += Bindable_BindingContextChanged;    
  24.             bindable.ItemAppearing += InfiniteListView_ItemAppearing;    
  25.         }    
  26.         private void Bindable_BindingContextChanged(object sender, EventArgs e) {    
  27.             OnBindingContextChanged();    
  28.         }    
  29.         protected override void OnBindingContextChanged() {    
  30.             base.OnBindingContextChanged();    
  31.             BindingContext = AssociatedObject.BindingContext;    
  32.         }    
  33.         protected override void OnDetachingFrom(ListView bindable) {    
  34.             base.OnDetachingFrom(bindable);    
  35.             bindable.BindingContextChanged -= Bindable_BindingContextChanged;    
  36.             bindable.ItemAppearing -= InfiniteListView_ItemAppearing;    
  37.         }    
  38.         void InfiniteListView_ItemAppearing(object sender, ItemVisibilityEventArgs e) {    
  39.             var items = AssociatedObject.ItemsSource as IList;    
  40.             if (items != null && e.Item == items[items.Count - 1]) {    
  41.                 if (LoadMoreCommand != null && LoadMoreCommand.CanExecute(null)) LoadMoreCommand.Execute(null);    
  42.             }    
  43.         }    
  44.     }    
  45. }    

 

The above code will create a ‘LoadMoreCommandProperty’ which is a type of command and will be invoked when the last item of list is diaplayed. We use OnAttachedTo and OnDetachingFrom methods to bind our context and invoke the command when ListView ItemAppearing event is triggered on last Item. Now that our bindable property is complete we need to create a listview in MainPage to get the functionality working.

But before that add a repository class which will return data page wise,

  1. public class NewsRepository {    
  2.     public List < string > News {    
  3.         get;    
  4.         set;    
  5.     }    
  6.     public NewsRepository() {    
  7.         News.Add("1");    
  8.         News.Add("1");    
  9.         News.Add("1");    
  10.         News.Add("1");    
  11.         News.Add("1");    
  12.         News.Add("1");    
  13.         News.Add("1");    
  14.         News.Add("1");    
  15.         News.Add("1");    
  16.         News.Add("1");    
  17.         News.Add("2");    
  18.         News.Add("2");    
  19.         News.Add("3");    
  20.         News.Add("4");    
  21.         News.Add("1");    
  22.         News.Add("1");    
  23.         News.Add("1");    
  24.         News.Add("1");    
  25.         News.Add("1");    
  26.         News.Add("1");    
  27.     }    
  28.     public List < string > getNews(int page) {    
  29.         return News.Take(10).Skip(page - 1).ToList();    
  30.     }    
  31. }    

 

As we are using MVVM pattern we will create a new ViewModel which will contain a command and a List which contains the items of ListView. For this create a folder ViewModel and add class NewsViewModel. NewsViewModel will get data from the repository and notify when new data is loaded to listview.

Code in NewsViewModel

  1. using System.Collections.Generic;    
  2. using System.ComponentModel;    
  3. using System.Runtime.CompilerServices;    
  4. using System.Windows.Input;    
  5. using Xamarin.Forms;    
  6. namespace InfiniteScrollApp.ViewModel {    
  7.     public class NewsViewModel: INotifyPropertyChanged {    
  8.         public event PropertyChangedEventHandler PropertyChanged;    
  9.         public List < string > News {    
  10.             get;    
  11.             set;    
  12.         }    
  13.         public ICommand LoadMore {    
  14.             get;    
  15.             set;    
  16.         }    
  17.         public NewsViewModel() {    
  18.             var repo = new NewsRepository();    
  19.             this.News = repo.getNews(1);    
  20.             OnpropertyChanged("News");    
  21.             int page = 2;    
  22.             this.LoadMore = new Command(() => {    
  23.                 var newNews = repo.getNews(1);    
  24.                 page += 1;    
  25.                 foreach(var item in newNews) {    
  26.                     News.Add(item);    
  27.                     OnpropertyChanged("News");    
  28.                 }    
  29.             });    
  30.         }    
  31.         void OnpropertyChanged([CallerMemberName] string propertyName = null) {    
  32.             var handler = PropertyChanged;    
  33.             if (handler != null) {    
  34.                 handler(thisnew PropertyChangedEventArgs(propertyName));    
  35.             }    
  36.         }    
  37.     }    
  38. }    

 

Now, Finally add a listview in MainPage,

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
  4.              xmlns:local="clr-namespace:InfiniteScrollApp"  
  5.              x:Class="InfiniteScrollApp.MainPage"  
  6.              xmlns:b="clr-namespace:InfiniteScrollApp.Behaviors">  
  7.   
  8.     <ListView ItemsSource="{Binding News}">  
  9.         <ListView.Behaviors>  
  10.             <b:ListViewinfiniteScroll LoadMoreCommand="{Binding LoadMore}" />  
  11.         </ListView.Behaviors>  
  12.         <ListView.ItemTemplate>  
  13.             <DataTemplate>  
  14.                 <ViewCell>  
  15.                     <ViewCell.View>  
  16.                         <Label Text="{Binding .}" />  
  17.                     </ViewCell.View>  
  18.                 </ViewCell>  
  19.             </DataTemplate>  
  20.         </ListView.ItemTemplate>  
  21.     </ListView>  
  22.   
  23. </ContentPage>  

Now set the BindingContext to new instance of NewsViewModel.

  1. public MainPage() {    
  2.      InitializeComponent();    
  3.      this.BindingContext = new NewsViewModel();    
  4. }    

 

Now if you run the app you will get new data as you scroll and you can use rest service in place of repository in your application.