How To Detect Listview Scroll State (Idle/Running) In Xamarin.Forms?

In xamarin.forms, their is no event available to check listview scroll state. So, in that case, we have to customize the Listview control in xamarin.forms. Following is the sample code to detect scroll state of listview:


Portable Library: In a portable library project, add class “CustomListview.cs”
 public class CustomListview : ListView
{
    public CustomListview() { }
    public CustomListview(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy) { }
    public event EventHandler<ScrollStateChangedEventArgs> ScrollStateChanged;
    public static void OnScrollStateChanged(object sender, ScrollStateChangedEventArgs e)
    {
        var customListview = (CustomListview)sender;
        customListview.ScrollStateChanged?.Invoke(customListview, e);
    }
    public event EventHandler<ItemTappedIOSEventArgs> ItemTappedIOS;
    public static void OnItemTappedIOS(object sender, ItemTappedIOSEventArgs e)
    {
        var customListview = (CustomListview)sender;
        customListview.ItemTappedIOS?.Invoke(customListview, e);
    }
}
public class ScrollStateChangedEventArgs : EventArgs
{
    public ScrollStateChangedEventArgs(ScrollState scrollState)
    {
        this.CurScrollState = scrollState;
    }
    public enum ScrollState
    {
        Idle = 0,
        Running = 1
    }
    public ScrollState CurScrollState { get; }
}


Android: In Xamarin.Android project, add class “CustomListviewRenderer.cs”
public class CustomListviewRenderer : ListViewRenderer
{
    public CustomListviewRenderer(Context context) : base(context) { }
    public static void Init() { }
    protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
    {
        base.OnElementChanged(e);
        var customListView = Element as CustomListview;
        if (customListView == null)
            return;
        Control.ScrollStateChanged += (s, ev) =>
        {
            var currentScrollState = ScrollStateChangedEventArgs.ScrollState.Idle;
            if (ev.ScrollState == Android.Widget.ScrollState.Idle)
            {
                currentScrollState = ScrollStateChangedEventArgs.ScrollState.Idle;
            }
            else
            {
                currentScrollState = ScrollStateChangedEventArgs.ScrollState.Running;
            }
            CustomListview.OnScrollStateChanged(customListView, new ScrollStateChangedEventArgs(currentScrollState));
        };
    }
}


iOS: In Xamarin.iOS project, add class “CustomListviewRenderer.cs”
public class CustomListviewRenderer : ListViewRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
    {
        base.OnElementChanged(e);
        var customListview = Element as CustomListview;
        if (customListview == null)
            return;
        if (Control != null)
        {
            var tvDelegate = new TableViewDelegate();
            Control.Delegate = tvDelegate;
            tvDelegate.OnDecelerationStarted += (s, ev) =>
            {
                CustomListview.OnScrollStateChanged(customListview,
                    new ScrollStateChangedEventArgs(ScrollStateChangedEventArgs.ScrollState.Running));
            };
            tvDelegate.OnDecelerationEnded += (s, ev) =>
            {
                CustomListview.OnScrollStateChanged(customListview,
                    new ScrollStateChangedEventArgs(ScrollStateChangedEventArgs.ScrollState.Idle));
            };
            tvDelegate.OnRowSelected += (s, ev) =>
            {
                var index = s as NSIndexPath;
                CustomListview.OnItemTappedIOS(customListview, new ItemTappedIOSEventArgs(index.Row));
            };
        }
    }
    /// <summary>
    /// Problem: Event registration is overwriting existing delegate. Either just use events or your own delegate:
    /// Solution: Create your own delegate and overide the required events
    /// </summary>
    public class TableViewDelegate : UIKit.UITableViewDelegate
    {
        public event EventHandler OnDecelerationEnded;
        public event EventHandler OnDecelerationStarted;
        public event EventHandler OnDidZoom;
        public event EventHandler OnDraggingStarted;
        public event EventHandler OnDraggingEnded;
        public event EventHandler OnScrollAnimationEnded;
        public event EventHandler OnScrolled;
        public event EventHandler OnScrolledToTop;
        public event EventHandler OnRowSelected;
        public override void DecelerationEnded(UIKit.UIScrollView scrollView)
        {
            OnDecelerationEnded?.Invoke(scrollView, null);
        }
        public override void DecelerationStarted(UIKit.UIScrollView scrollView)
        {
            OnDecelerationStarted?.Invoke(scrollView, null);
        }
        public override void DidZoom(UIKit.UIScrollView scrollView)
        {
            OnDidZoom?.Invoke(scrollView, null);
        }
        public override void DraggingStarted(UIKit.UIScrollView scrollView)
        {
            OnDraggingStarted?.Invoke(scrollView, null);
        }
        public override void DraggingEnded(UIKit.UIScrollView scrollView, bool willDecelerate)
        {
            OnDraggingEnded?.Invoke(scrollView, null);
        }
        public override void ScrollAnimationEnded(UIKit.UIScrollView scrollView)
        {
            OnScrollAnimationEnded?.Invoke(scrollView, null);
        }
        public override void Scrolled(UIKit.UIScrollView scrollView)
        {
            OnScrolled?.Invoke(scrollView, null);
        }
        public override void ScrolledToTop(UIKit.UIScrollView scrollView)
        {
            OnScrolledToTop?.Invoke(scrollView, null);
        }
        public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
        {
            OnRowSelected?.Invoke(indexPath, null);
        }
    }
}


How To Use Above Scroll State Event In Xaml?

1. Add “DemoCustomListview.xaml”


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:control="clr-namespace:TestApp.CustomRenderer"
             x:Class="TestApp.DemoCustomListview">
    <ContentPage.Content>
        <control:CustomListview x:Name="listViewDemo">
            <x:Arguments>
                <ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
            </x:Arguments>
            <control:CustomListview.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label Text="{Binding Name}"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </control:CustomListview.ItemTemplate>
        </control:CustomListview>
    </ContentPage.Content>
</ContentPage>

2. In code behind, do the following:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DemoCustomListview : ContentPage
{
    public DemoCustomListview()
    {
        InitializeComponent();
        listViewDemo.ScrollStateChanged += OnListScrollStateChanged;
    }
    private void OnListScrollStateChanged(object sender, CustomRenderer.ScrollStateChangedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(e.CurScrollState);
    }
}

Comments

Popular posts from this blog

How To Upload And Save Image Or File Using Asp.Net WebApi? – Server Side (Part 1)

Generic Web Api Client Request (GET/POST)