Xamarin Forms- Let's Build Web-like Pagination
Tags: xamarinforms
Published: 2021-07-28 20:49:15

---
title: Xamarin Forms- Let's Build Web-like Pagination
published: true
description: Reusable Pager Component for Xamarin.Forms
tags: XamarinForms
//cover_image:
---
I was working with more than 1000 rows and had to implement pagination in Xamarin Forms. I made use of ContentView and made it reusable. We will be using **FontAwesome** icons to add icons to our navigation button in pager. Let's dive into building it from scratch

### Getting Started 
Install **Xamarin.CommunityToolkit** nuget package from Nuget Package Manager in all projects of a solution . This package is used to implement **EventToCommandBehaviour** and **asynchronous Command** for Pagination View.

## Building Pagination View
1. Right Click on Shared Project and Add ContentView (xaml) and name it to  **PaginationView**.
2. To add FontAwesome icons to our project , visit the [link](https://fontawesome.com/v5.15/how-to-use/on-the-desktop/setup/getting-started) and download it. Add  downloaded files to our Shared project and set properties of those files as **EmbeddedResource**. Export font in Assembly level by adding below line before namespace of PaginationView
>   [assembly: ExportFont("FontAwesomeSolid.otf", Alias = "FontAwesomeSolid")]
3. In order to make this view reusable, we will declare Foreground Color of Navigation buttons, Disabled Color, Current active page and Total Number of pages as BindableProperty. Let's get this done in Code-behind of PaginationView

 ```public static readonly BindableProperty CurrentPageProperty = BindableProperty.Create(nameof(CurrentPage), typeof(int), typeof(PaginationView), defaultValue: 1, propertyChanged: CurrentPagePropertyChanged);

        public static readonly BindableProperty PageCountProperty = BindableProperty.Create(nameof(PageCount), typeof(int), typeof(PaginationView), defaultValue: 1, propertyChanged: PageCountPropertyChanged);

        public static readonly BindableProperty DisabledColorProperty = BindableProperty.Create(nameof(DisabledColor), typeof(Color), typeof(PaginationView), defaultValue: Color.Gray, propertyChanged: OnDisabledColorPropertyChanged);


        public static readonly BindableProperty IconBackgroundColorProperty = BindableProperty.Create(nameof(IconBackgroundColor), typeof(Color), typeof(PaginationView), defaultValue: Color.Green, propertyChanged: IconBackgroundColorPropertyChanged);

       public static readonly BindableProperty OnPaginatedProperty = BindableProperty.Create(nameof(OnPaginated), typeof(IAsyncCommand<int>), typeof(PaginationView));

       private static void OnDisabledColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            ((PaginationView)bindable).SetPageNavigationValues();
        }
        private static void IconBackgroundColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            ((PaginationView)bindable).SetPageNavigationValues();
        }

        private static void PageCountPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            ((PaginationView)bindable).SetPageNavigationValues();
        }

        private static void CurrentPagePropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            ((PaginationView)bindable).SetPageNavigationValues();
        }
        public Color DisabledColor
        {
            get => (Color)GetValue(DisabledColorProperty);
            set => SetValue(DisabledColorProperty, value);
        }

        public Color IconBackgroundColor
        {
            get => (Color)GetValue(IconBackgroundColorProperty);
            set => SetValue(IconBackgroundColorProperty, value);
        }

        public int CurrentPage
        {
            get => (int)GetValue(CurrentPageProperty);
            set => SetValue(CurrentPageProperty, value);
        }

        public int PageCount
        {
            get => (int)GetValue(PageCountProperty);
            set => SetValue(PageCountProperty, value);
        }

        public IAsyncCommand<int> OnPaginated
        {
            get => (IAsyncCommand<int>)GetValue(OnPaginatedProperty);
            set => SetValue(OnPaginatedProperty, value);
        }```

Here are a couple of things going on. First we declared all the properties that are bindable and we declared backing properties for them.

>  Backing field must have its name same as its bindable property without 'Property' text in it.

For Data binding, we need to implement **INotifyPropertyChanged** in our PaginationView.

We declared a bindable property named OnPaginatedProperty . This is of type **IAsyncCommand** and is a callback command in view that Uses Pagination view in it. 

With bindable properties in place, we can now write our logic to manipulate our Pagination view   

Whenever navigation reaches to first page, move to first page and navigate backwards buttons are disabled and when navigation reaches last page, move to last page and navigate forward buttons are disabled. We can achieve this logic by writing 

      private async ValueTask GetLastPageData()
        {
            if (CurrentPage == PageCount)
                return;
            CurrentPage = PageCount;
            SetPageNavigationValues();
            await ExecuteCommand();
        }

        private async ValueTask GetNextPageData()
        {
            if (CurrentPage == PageCount)
                return;
            CurrentPage += 1;
            SetPageNavigationValues();
            await ExecuteCommand();
        }

        private async ValueTask GetPreviousPageData()
        {
            if (CurrentPage == 1)
                return;
            CurrentPage -= 1;
            SetPageNavigationValues();
            await ExecuteCommand();
        }

        private async ValueTask GetFirstPageData()
        {
            if (CurrentPage == 1)
                return;
            CurrentPage = 1;
            SetPageNavigationValues();
            await ExecuteCommand();
        }

        private async Task ExecuteCommand()
        {
            if (OnPaginated != null)
                await OnPaginated.ExecuteAsync(CurrentPage);
        }

        private bool _allowPreviousPageNavigation;
        public bool AllowPreviousPageNavigation
        {
            get => _allowPreviousPageNavigation;
            set
            {
                _allowPreviousPageNavigation = value;
                PreviousPageButtonForegroundColor = value ? IconBackgroundColor : DisabledColor;
                OnPropertyChanged(nameof(AllowPreviousPageNavigation));
            }
        }
        private bool _allowFirstPageNavigation;
        public bool AllowFirstPageNavigation
        {
            get => _allowFirstPageNavigation;
            set
            {
                _allowFirstPageNavigation = value;

                FirstPageButtonForegroundColor = value ? IconBackgroundColor : DisabledColor;
                OnPropertyChanged(nameof(AllowFirstPageNavigation));
            }
        }

        private bool _allowNextPageNavigation;
        public bool AllowNextPageNavigation
        {
            get => _allowNextPageNavigation;
            set
            {
                _allowNextPageNavigation = value;
                NextPageButtonForegroundColor = value ? IconBackgroundColor : DisabledColor;
                OnPropertyChanged(nameof(AllowNextPageNavigation));
            }
        }
        private bool _allowLastPageNavigation;
        public bool AllowLastPageNavigation
        {
            get => _allowLastPageNavigation;
            set
            {
                _allowLastPageNavigation = value;
                LastPageButtonForegroundColor = value ? IconBackgroundColor : DisabledColor;
                OnPropertyChanged(nameof(AllowLastPageNavigation));
            }
        }

        private void SetPageNavigationValues()
        {
            AllowFirstPageNavigation = true;
            AllowNextPageNavigation = true;
            AllowLastPageNavigation = true;
            AllowPreviousPageNavigation = true;
            if (CurrentPage == 1)
            {
                AllowPreviousPageNavigation = false;
                AllowFirstPageNavigation = false;
            }
            if (CurrentPage == PageCount)
            {
                AllowNextPageNavigation = false;
                AllowLastPageNavigation = false;
            }
        }

Now we just need to bind these functions to Command that gets called from our Pagination Component when we click navigation button in it. This can be achieved by initialising our commands in constructor

       public IAsyncValueCommand MoveToFirstPageCommand { get; set; }
        public IAsyncValueCommand MoveToPreviousPageCommand { get; set; }
        public IAsyncValueCommand MoveToNextPageCommand { get; set; }
        public IAsyncValueCommand MoveToLastPageCommand { get; set; }

        public PaginationView()
        {
            InitializeComponent();
            MoveToFirstPageCommand = new AsyncValueCommand(() => GetFirstPageData(), allowsMultipleExecutions: false);
            MoveToPreviousPageCommand = new AsyncValueCommand(() => GetPreviousPageData(), allowsMultipleExecutions: false);
            MoveToNextPageCommand = new AsyncValueCommand(() => GetNextPageData(), allowsMultipleExecutions: false);
            MoveToLastPageCommand = new AsyncValueCommand(() => GetLastPageData(), allowsMultipleExecutions: false);
            BindingContext = this;
        }

Now, lets get into design part of our ContentView.

        <?xml version="1.0" encoding="UTF-8"?>
       <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Xam.Views.Pagination.PaginationView"
                xmlns:xct="http://xamarin.com/schemas/2020/toolkit">
    <StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand" Margin="0,0,0,10">
        <ImageButton HeightRequest="40" BackgroundColor="Transparent" IsEnabled="{Binding AllowFirstPageNavigation}">
            <ImageButton.Source>
                <FontImageSource FontFamily="FontAwesomeSolid"
                      Color="{Binding FirstPageButtonForegroundColor}"
                     Glyph="&#xf100;"/>
            </ImageButton.Source>

            <ImageButton.Behaviors>
                <xct:EventToCommandBehavior EventName="Clicked" Command="{Binding MoveToFirstPageCommand}"></xct:EventToCommandBehavior>
            </ImageButton.Behaviors>
        </ImageButton>
        <ImageButton HeightRequest="40" BackgroundColor="Transparent" IsEnabled="{Binding AllowPreviousPageNavigation}">
            <ImageButton.Source>
                <FontImageSource FontFamily="FontAwesomeSolid"
                      Color="{Binding PreviousPageButtonForegroundColor}"
                     Glyph="&#xf104;"/>
            </ImageButton.Source>
            <ImageButton.Behaviors>
                <xct:EventToCommandBehavior EventName="Clicked" Command="{Binding MoveToPreviousPageCommand}"></xct:EventToCommandBehavior>
            </ImageButton.Behaviors>
        </ImageButton>
        <Frame CornerRadius="20" HorizontalOptions="Start" WidthRequest="30" VerticalOptions="Center" Margin="0" Padding="10" BackgroundColor="{Binding IconBackgroundColor}">
            <Label Text="{Binding CurrentPage}" TextColor="White" HorizontalOptions="Center" VerticalOptions="Center" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" FontAttributes="Bold" />
        </Frame>
        <Label Text="{Binding PageCount,StringFormat='Of {0}'}" VerticalOptions="Center" FontAttributes="Bold"></Label>
        <ImageButton HeightRequest="40" BackgroundColor="Transparent" IsEnabled="{Binding AllowNextPageNavigation}">
            <ImageButton.Source>
                <FontImageSource FontFamily="FontAwesomeSolid"
                      Color="{Binding NextPageButtonForegroundColor}"
                     Glyph="&#xf105;"/>
            </ImageButton.Source>
            <ImageButton.Behaviors>
                <xct:EventToCommandBehavior EventName="Clicked" Command="{Binding MoveToNextPageCommand}"></xct:EventToCommandBehavior>
            </ImageButton.Behaviors>
        </ImageButton>
        <ImageButton HeightRequest="40" BackgroundColor="Transparent" IsEnabled="{Binding AllowLastPageNavigation}">
            <ImageButton.Source>
                <FontImageSource FontFamily="FontAwesomeSolid"
                       Color="{Binding LastPageButtonForegroundColor}"
                     Glyph="&#xf101;"/>
            </ImageButton.Source>
            <ImageButton.Behaviors>
                <xct:EventToCommandBehavior EventName="Clicked" Command="{Binding MoveToLastPageCommand}"></xct:EventToCommandBehavior>
            </ImageButton.Behaviors>
        </ImageButton>
    </StackLayout>
</ContentView>

Here, we are placing buttons and labels horizontally in  StackLayout and binding events and properties to it.

### Reusing the build ContentView
1. Add xmlns in xaml and give it a name.

        xmlns:customViews="clr-namespace:Xam.Views.Pagination" 
        x:Name="Page"
2. Use the component wherever required, in the page
 
        <customViews:PaginationView 
                            CurrentPage="{Binding Source={x:Reference Page}, Path=BindingContext.PageNumberBackingField}"
                            PageCount="{Binding Source={x:Reference Page}, Path=BindingContext.PageCountBackingField}" 
                            OnPaginated="{Binding Source={x:Reference Page}, Path=BindingContext.OnPaginatedCommand}"
                            IconBackgroundColor="Red"
                            DisabledColor="Gray"/>

OnPaginatedCommand should be of type IAsyncComand<int> where parameter int gives us the page number we are in.

![Working GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/so2gyivbjzxi8e8u4x2c.gif)
### Resources
You can find a link to Github repo with the code for this [post](https://github.com/solo-developer/Xamarin.Views.Pagination)