Implementing ISupportIncrementalLoading interface - UWP - C#
<p>I am learning C# and currently working on <code>ISupportIncrementalLoading</code> in UWP app but cant seem to get a hang of it. It gives me errors even before compiling it. I got the idea from <a href="https://marcominerva.wordpress.com/2013/05/22/implementing-the-isupportincrementalloading-interface-in-a-window-store-app/#" rel="nofollow noreferrer">here</a>. But I am unable to understand as how I can implement it in my application.</p>

<p>I have a <code>IIncrementalLoading</code> interface and a separate class which dynamically adds content to an <code>ObservableCollection&lt;T&gt;</code>. If I don't implement <code>Incremental Loading</code> my <code>ObservableCollection</code> dynamically provides content to my <code>SearchPage.xaml</code> in <code>GridView</code> but if I load more items they replace previously generated items and only the new items are shown instead.</p>

<p>Code in <code>SearchPage.xaml</code>:<br/></p>

<pre><code>&lt;Page
x:Class="WatchfreeWebsite.SearchPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WatchfreeWebsite"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:WatchfreeWebsite"
mc:Ignorable="d"&gt;

&lt;Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"&gt;

    &lt;Grid.RowDefinitions&gt;
        &lt;RowDefinition Height="Auto"/&gt;
        &lt;RowDefinition Height="Auto"/&gt;
        &lt;RowDefinition/&gt;
    &lt;/Grid.RowDefinitions&gt;

    &lt;StackPanel&gt;
        &lt;TextBlock Text="Search Results"
                   FontSize="20"
                   TextAlignment="Center"/&gt;
        &lt;TextBlock Text="Search for Movies and TV Shows"
                   TextAlignment="Center"
                   TextWrapping="Wrap"/&gt;
        &lt;HyperlinkButton x:Name="IndexPageLink"
                         Content="Go to Index"
                         HorizontalAlignment="Center"
                         Click="IndexPageLink_Click"/&gt;
    &lt;/StackPanel&gt;
    &lt;StackPanel Grid.Row="1"&gt;
        &lt;TextBox x:Name="SearchInputBox" 
                 TextAlignment="Left"
                 Width="280"
                 PlaceholderText="search"
                 TextChanged="SearchInputBox_TextChanged"&gt;&lt;/TextBox&gt;
        &lt;TextBlock x:Name="ErrorTextBox"
                   TextWrapping="Wrap"
                   TextAlignment="Center"/&gt;
    &lt;/StackPanel&gt;
    &lt;GridView Grid.Row="2"
              x:Name="SearchGrid"
              HorizontalAlignment="Center"
              ItemsSource="{x:Bind SearchList, Mode=OneWay}"
              IsItemClickEnabled="True"
              ItemClick="SearchGrid_ItemClick"&gt;
        &lt;GridView.Header&gt;
            &lt;StackPanel BorderBrush="Red"
                        BorderThickness="0,0,0,1"
                        Margin="5"
                        HorizontalAlignment="Center"&gt;
                &lt;TextBlock Text=""
                           x:Name="SearchGridHeader"
                           TextAlignment="Center"
                           Margin="0"/&gt;
            &lt;/StackPanel&gt;
        &lt;/GridView.Header&gt;
        &lt;GridView.ItemTemplate&gt;
            &lt;DataTemplate x:DataType="data:SearchItems"&gt;
                &lt;StackPanel BorderThickness="0,1,0,0"
                            BorderBrush="Red"
                            Margin="5"&gt;
                    &lt;TextBlock Text="{x:Bind SearchTitle, Mode=OneWay}"
                               TextAlignment="Center"
                               TextWrapping="Wrap"
                               Width="140"
                               Height="40"/&gt;
                    &lt;StackPanel BorderBrush="Red" BorderThickness="0,0,0,1"
                                Margin="0"&gt;
                        &lt;Image x:Name="CoverImage"
                               Source="{x:Bind SearchImageLink, Mode=OneWay}"
                               Width="130"
                               Height="200"
                               Margin="0"/&gt;
                    &lt;/StackPanel&gt;
                &lt;/StackPanel&gt;
            &lt;/DataTemplate&gt;
        &lt;/GridView.ItemTemplate&gt;
        &lt;GridView.Footer&gt;
            &lt;HyperlinkButton x:Name="MoreItemsLink"
                             Content="Load more items"
                             Click="MoreItemsLink_Click"
                             Visibility="Collapsed"/&gt;
        &lt;/GridView.Footer&gt;
    &lt;/GridView&gt;
&lt;/Grid&gt;
</code></pre>

<p></p>

<p>Code in <code>SearchPage.xaml.cs</code>:<br/></p>

<pre><code>using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using HtmlAgilityPack;
using static WatchfreeWebsite.HtmlDocs;



namespace WatchfreeWebsite
{
public sealed partial class SearchPage : Page
{
    HtmlDocument SearchDoc;
    public static string[] SearchLinks = new string[500];
    public static string[] SearchTitles = new string[500];
    public static string[] SearchImageLinks = new string[500];
    public static string[] SearchNextPagesLinks = new string[50];

    public static int numberOfLinks = 0;
    int numberOfTitles = 0;
    int numberOfImages = 0;
    int nextPage = 2;
    int numberOfPages = 0;
    public static int lastListItems = 0;


    //IIncrementalSource&lt;SearchItems&gt; SearchList = new IncrementalLoadingCollection&lt;GetTVShows(),SearchItems&gt;();

    int listItems = 0;
    DispatcherTimer sTimer = new DispatcherTimer();

    public SearchPage()
    {
        this.InitializeComponent();
        NavigationCacheMode = NavigationCacheMode.Enabled;
    }

    private void DeleteList(int count)
    {
        for (int x = 0; x &lt; count; x++)
        {
            SearchList.RemoveAt(0);
        }
    }

    private async void LoadHtmlDocument(string url)
    {
        try
        {
            if (NetworkInformatiom())
            {
                SearchDoc = await new HtmlWeb().LoadFromWebAsync(url);
            }
            else
            {
                SearchDoc = null;
            }
            if (SearchDoc != null)
            {
                Links(SearchDoc);
            }
        }
        catch (Exception ex)
        {
            ErrorDialog("SearchDoc_Download_Failed\n" + ex.Message);
        }
    }

    private void Links(HtmlDocument doc)
    {
        listItems = 0;
        try
        {
            foreach (var link in doc.DocumentNode.Descendants("a").Where(pl =&gt; pl.Attributes.Contains("href")))
            {
                string dataValue = link.GetAttributeValue("href", "");
                //dataValue = ReplaceLinkStrings(dataValue);
                if (dataValue.StartsWith("/watch") &amp;&amp; dataValue.EndsWith(".html"))
                {
                    SearchLinks[numberOfLinks] = MoviesPage.MoviesBaseLink + dataValue.Trim();
                    listItems++;
                    numberOfLinks++;
                }
            }
            ImageLinks(doc);
        }
        catch (Exception ex)
        {
            ErrorDialog("There was a problem while acquiring search links.\n" + ex.Message);
        }
    }

    private void ImageLinks(HtmlDocument doc)
    {
        try
        {
            //int LinkID = 0;
            foreach (var link in doc.DocumentNode.Descendants("img").Where(d =&gt; d.Attributes.Contains("src")))
            {
                if (link != null)
                {
                    if (link.Attributes["src"].Value == "/images/noposter.jpg")
                    {
                        SearchImageLinks[numberOfImages] = @"ms-appx:///Assets/noposter.jpg";
                    }
                    else
                    {
                        SearchImageLinks[numberOfImages] = string.Format("http:{0}", link.Attributes["src"].Value);
                    }
                    numberOfImages++;
                }
                if (numberOfImages == numberOfLinks)
                {
                    break;
                }
            }
            Titles(doc);
        }
        catch (Exception ex)
        {
            ErrorDialog("There was a problem in Image links.\n" + ex.Message);
        }
    }

    public void Titles(HtmlDocument doc)
    {
        try
        {
            //int a = 0;
            foreach (var link in doc.DocumentNode.Descendants("a").Where(t =&gt; t.Attributes.Contains("title")))
            {
                if (link != null &amp;&amp; link.InnerText != "WatchFree.to" &amp;&amp; link.InnerText != "Movies" &amp;&amp; link.InnerText != "TV Shows")
                {
                    string x = link.GetAttributeValue("title", null);
                    x = x.Replace("Watch Putlocker", "");
                    x = x.Trim();
                    SearchTitles[numberOfTitles] = x;
                    numberOfTitles++;
                    if (numberOfTitles == numberOfLinks)
                    {
                        break;
                    }
                }
            }
            SearchList = SearchManager.GetTVShows(lastListItems, numberOfLinks);
            // += or =       we will find out
            this.Bindings.Update();
            lastListItems = numberOfLinks;
            CheckNextPageLinks(doc);
        }
        catch (Exception ex)
        {
            ErrorDialog("There was a problem while acquiring movie titles.\n" + ex.Message);
        }
    }

    private void SearchGrid_ItemClick(object sender, ItemClickEventArgs e)
    {
        var clickedItem = (SearchItems)e.ClickedItem;
        if (SearchImageLinks[clickedItem.SearchID - 1] != null &amp;&amp; clickedItem.SearchLink != null)
        {
            if (clickedItem.SearchLink.Contains("tv-show"))
            {
                string[] itemDetails = { clickedItem.SearchTitle, SearchImageLinks[(clickedItem.SearchID - 1)], clickedItem.SearchLink };
                Frame.Navigate(typeof(TVShowDetailsPage), itemDetails);
            }
            else if (clickedItem.SearchLink.Contains("movie"))
            {
                string[] itemDetails = { clickedItem.SearchTitle, SearchImageLinks[(clickedItem.SearchID - 1)], clickedItem.SearchLink };
                Frame.Navigate(typeof(MovieDetails), itemDetails);
            }
        }
        else
        {
            ;
        }
    }

    private void SearchInputBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        string input = SearchInputBox.Text.Trim();
        string output = "";
        if (input.Length &lt; 3)
        {
            SearchGridHeader.Text = "";
            if (SearchNextPagesLinks[0] != null || SearchLinks[0] != null || SearchImageLinks[0] != null || SearchTitles[0] != null)
            {
                DefaultVariablesValues();
            }
            if (SearchList != null)
            {
                if (SearchList.Count &gt; 0)
                { 
                    DeleteList((SearchList.Count));
                }
            }
        }
        if (!(input.Length &lt; 3))
        {
            //sTimer = new DispatcherTimer();
            if (!sTimer.IsEnabled)
            {
                sTimer.Interval = TimeSpan.FromSeconds(1);
                //sTimer.Start();
            }

            ErrorTextBox.Text = "";
            SearchGridHeader.Text = "Search Results";
            if (input.Contains(" "))
            {
                output = input.Replace(" ", "+");
                output = MoviesPage.MoviesBaseLink + @"/?keyword=" + output + @"&amp;search_section=1";
                ErrorTextBox.Text = output;
                ErrorTextBox.IsTextSelectionEnabled = true;
            }
            else
            {
                output = MoviesPage.MoviesBaseLink + @"/?keyword=" + input + @"&amp;search_section=1";
                //ErrorTextBox.Text = output;
            }
            SearchList = new ObservableCollection&lt;SearchItems&gt;();
            LoadHtmlDocument(output);

        }
        else
        {
            ErrorTextBox.Text = "the length of the input should not be less than 3";
            if (input.Length == 0)
            {
                ErrorTextBox.Text = "";
            }
            if (sTimer.IsEnabled)
                sTimer.Stop();
        }
    }

    private void IndexPageLink_Click(object sender, RoutedEventArgs e)
    {
        Frame.Navigate(typeof(IndexPage));
    }

    private void MoreItemsLink_Click(object sender, RoutedEventArgs e)
    {
        LoadHtmlDocument(MoviesPage.MoviesBaseLink + SearchNextPagesLinks[numberOfPages - 1]);
    }

    private void DefaultVariablesValues()
    {
        numberOfLinks = 0;
        numberOfTitles = 0;
        numberOfImages = 0;
        nextPage = 2;
        numberOfPages = 0;
        lastListItems = 0;
        int a = 0;
        foreach (var item in SearchLinks)
        {
            if (item != null)
            {
                SearchLinks[a] = null;
            }
            a++;
        }
        a = 0;
        foreach (var item in SearchTitles)
        {
            if (item != null)
            {
                SearchTitles[a] = null;
            }
            a++;
        }
        a = 0;
        foreach (var item in SearchImageLinks)
        {
            if (item != null)
            {
                SearchImageLinks[a] = null;
            }
            a++;
        }
        a = 0;
        foreach (var item in SearchNextPagesLinks)
        {
            if (item != null)
            {
                SearchNextPagesLinks[a] = null;
            }
            a++;
        }
    }

    private void CheckNextPageLinks(HtmlDocument doc)
    {
        string lastLink = "";
        foreach (var link in doc.DocumentNode.Descendants("a").Where(t =&gt; t.Attributes.Contains("href")))
        {
            string value = link.GetAttributeValue("href", "");
            if (value.StartsWith("/?keyword") &amp;&amp; value.Contains("page=" + nextPage))
            {
                SearchNextPagesLinks[numberOfPages] = value;
                lastLink = value;
                numberOfPages++;
                break;
            }
        }
        //--------------------------------------
        // checking for the availability of next page
        if (lastLink.StartsWith(@"/?keyword") &amp;&amp; lastLink.Contains("page=" + nextPage))
        {
            MoreItemsLink.Visibility = Visibility.Visible;
        }
        else
        {
            MoreItemsLink.Visibility = Visibility.Collapsed;
        }
        //--------------------------------------
        foreach (var item in SearchNextPagesLinks)
        {
            if (item == lastLink)
            {
                nextPage++;
            }
        }

    }
}
}
</code></pre>

<p>Code in <code>IncrementalLoadingInterface</code>:<br/></p>

<pre><code>using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;

namespace WatchfreeWebsite
{
public interface IIncrementalSource&lt;T&gt;
{
    Task&lt;IEnumerable&lt;T&gt;&gt; GetPagedItems(int pageIndex, int pageSize);
}

public class IncrementalLoadingCollection&lt;T, I&gt; : ObservableCollection&lt;I&gt;,
 ISupportIncrementalLoading
 where T : IIncrementalSource&lt;I&gt;, new()
{
    private T source;
    private int itemsPerPage;
    private bool hasMoreItems;
    private int currentPage;

    public IncrementalLoadingCollection(int itemsPerPage = 1)
    {
        this.source = new T();
        this.itemsPerPage = itemsPerPage;
        this.hasMoreItems = true;
    }

    public bool HasMoreItems
    {
        get { return hasMoreItems; }
    }

    public IAsyncOperation&lt;LoadMoreItemsResult&gt; LoadMoreItemsAsync(uint count)
    {
        var dispatcher = Window.Current.Dispatcher;

        return Task.Run(
            async () =&gt;
            {
                uint resultCount = 0;
                var result = await source.GetPagedItems(currentPage++, itemsPerPage);

                if (result == null || result.Count() == 0)
                {
                    hasMoreItems = false;
                }
                else
                {
                    resultCount = (uint)result.Count();

                    await dispatcher.RunAsync(
                        CoreDispatcherPriority.Normal,
                        () =&gt;
                        {
                            foreach (I item in result)
                                this.Add(item);
                        });
                }

                return new LoadMoreItemsResult() { Count = resultCount };

            }).AsAsyncOperation();
    }
}
}
</code></pre>

<p>Code in <code>SearchItems</code> (separate class):<br/></p>

<pre><code>using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace WatchfreeWebsite
{
public class SearchItems
{
    public int SearchID
    {
        get; set;
    }

    public string SearchTitle
    {
        get; set;
    }

    public string SearchImageLink
    {
        get; set;
    }

    public string SearchLink { get; set; }
}

public class SearchManager : IIncrementalSource&lt;SearchItems&gt;
{
    private ObservableCollection&lt;SearchItems&gt; items;

    public async Task&lt;IEnumerable&lt;SearchItems&gt;&gt; GetPagedItems(int pageIndex, int pageSize)
    {
        return await Task.Run&lt;IEnumerable&lt;SearchItems&gt;&gt;(() =&gt;
        {
            if (SearchPage.lastListItems == 0 &amp;&amp; SearchPage.numberOfLinks == 0)
            {
                var shows = new ObservableCollection&lt;SearchItems&gt;();
                shows.Add(new SearchItems { SearchID = 1, SearchTitle = "No Search Results", SearchImageLink = @"ms-appx:///Assets/noposter.jpg" });
                return shows;
            }
            else
            {
                var result = GetTVShows(SearchPage.lastListItems, SearchPage.numberOfLinks); //(from p in persons select p).Skip(pageIndex * pageSize).Take(pageSize);
                return result;
            }

        });
    }

    public static ObservableCollection&lt;SearchItems&gt; GetTVShows(int start, int finish)
    {
        var movies = new ObservableCollection&lt;SearchItems&gt;();
        for (int x = start; x &lt; finish; x++)
        {
            movies.Add(new SearchItems { SearchID = (x + 1), SearchTitle = SearchPage.SearchTitles[x], SearchImageLink = SearchPage.SearchImageLinks[x], SearchLink = SearchPage.SearchLinks[x] });
        }

        //if (start == 0 &amp;&amp; finish == 0)
        {
            //movies.Add(new SearchItems { SearchID = 1, SearchTitle = "No Search Results", SearchImageLink = @"ms-appx:///Assets/noposter.jpg" });
        }
        return movies;
    }
}

}
</code></pre>

<p><strong>Explanation:</strong>
First before implementing incremental loading I call <code>GetTVShows()</code> with <code>start</code> and <code>finish</code> parameters to get the required amount of items. If the search doesn't get anything, both of them are <code>zero</code> so <code>GetTVShows()</code> only produces one item with <code>No Results</code>.</p>

<p>But in incremental loading I have to specify at compile time how many items I want, as it has to search for items I cannot specify how many items will be there but maximum of the items I get is 24 per request.</p>

<p>I even tried to use <code>+=</code> to my <code>SearchResults</code> but VS produces the error that this is not available with <code>ObservableCollection</code> so I searched internet and found the incremental loading. But if it's possible I want to add more results with a click of the link rather than scrolling through the edges. I cannot seem to understand how Incremental loading is working and how I need to call my <code>GetTVShows()</code> function to produce the desired results as it does now. </p>

<p>I also recall this from Twitter for Windows 10 as it uses incremental loading to get more DMs with a <code>hyperlink</code> click. But those DMs are already stored in a database so they are not dynamically added (at least that is what I think and I could be wrong.).</p>

<p>If someone can point out what I can do to make it work, that will be really appreciated. Any information to help me out or any links to point me in the right direction so I can understand the effects of how <code>ISupportIncrementalLoading</code> actually works, would be appreciated a lot. Thanks.</p>