[UWP]ListViewが終端までスクロールしたら自動追加読み込みさせる

## 目標
ListViewを一番下までスクロールしたとき、追加読み込みを自動実行させたい。
（ユーザーに追加読み込みのボタン操作を要求しない）

## 方法
ListView.IncrementalLoadingTrigger="Edge"を指定し、さらにListView.ItemsSourceにISupportIncrementalLoading を実装したコレクションを渡すことで自動追加読み込みが行われるようになります。




## ISupportIncrementalLoading を実装する
```csharp:追加読み込みのデータ取得用インターフェイス
public interface IIncrementalSource<T>
{
	Task<IEnumerable<T>> GetPagedItems(uint head, uint count);
}
```

```csharp:IncrementalLodingを実装したコレクション
public class IncrementalLoadingCollection<T, I> : ObservableCollection<I>,
		ISupportIncrementalLoading
		where T : IIncrementalSource<I>
{
	public event Action BeginLoading;
	public event Action CompleteLoading;

	// Note: Navigation操作に関わるバグへの対処
	// 読み込み中にナビゲーション等によって ListView の LayoutUpdate が阻害されると
	// IncrementalLoading 処理が呼び出し続けられてしまいます（※未検証）
	// これを防止するため、Page.NavigationTo/From で IsPuaseLoading をスイッチして対応してください
	// なおPage.NavigationFromでIncrementalLoadingCollectionをItemsSourceから外すとより確実に読み込みを一時停止できます
	public bool IsPuaseLoading { get; set; }
	private T _Source;
	private uint _ItemsPerPage;
	private bool _HasMoreItems;
	private uint _Position;



	public IncrementalLoadingCollection(T source, uint itemsPerPage = 20, uint firstHeadPosition = 1)
	{
		this._Source = source;
		this._ItemsPerPage = itemsPerPage;
		this._HasMoreItems = true;
		_Position = firstHeadPosition;
		IsPuaseLoading = false;
	}

		

	public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
	{
		var dispatcher = Window.Current.Dispatcher;

		return Task.Run(async () =>
		{
			uint resultCount = 0;
			List<I> resultItems = null;

			await dispatcher.RunAsync(CoreDispatcherPriority.Normal, 
				() => BeginLoading?.Invoke()
				);

			try
			{
				var items = await _Source.GetPagedItems(_Position, _ItemsPerPage);
				resultItems = items?.ToList();
			}
			catch (Exception ex)
			{
				Debug.WriteLine(ex.Message);
			}
				
			if (resultItems == null || resultItems.Count == 0)
			{
				_HasMoreItems = false;
			}
			else
			{
				resultCount = (uint)resultItems.Count;

				_Position += resultCount;

				await dispatcher.RunAsync(CoreDispatcherPriority.Normal, 
					() =>
					{
						foreach (I item in resultItems)
							this.Add(item);
					});

			}

			await dispatcher.RunAsync(CoreDispatcherPriority.Normal,
				() => DoneLoading?.Invoke()
				);

			// 多重読み込み防止のため
			// リスト表示に反映されるまで
			// タスクの終了を遅延させる必要があります
			await Task.Delay(500);

			return new LoadMoreItemsResult() { Count = resultCount };
		})
		.AsAsyncOperation();
	}

	protected override void ClearItems()
	{
		base.ClearItems();

		// Note: PullToRefresh等で要素を削除した時のための対応
		// IIncrementalSourceの実装で head == 1 の時に
		// 強制的にアイテムソースのリストを更新させるよう対応してください
		_Position = 1;		
	}

	public bool HasMoreItems
	{
		get { return !IsPuaseLoading && _HasMoreItems; }
	}
}
```
## 使用例
実際に検索機能としてインクリメンタルロードを利用しているコードです。
（リンク切れになっていたらプロジェクトページから検索してみてください）

XAML
https://github.com/tor4kichi/Hohoema/blob/develop/NicoPlayerHohoema/Views/SearchPage.xaml

ViewModel
https://github.com/tor4kichi/Hohoema/blob/develop/NicoPlayerHohoema/ViewModels/HohoemaVideoListingPageViewModelBase.cs


## 参考
* [ISupportIncrementalLoading interface](https://msdn.microsoft.com/library/windows/apps/hh701916)

* [ISupportIncrementalLoading only fires once](http://stackoverflow.com/questions/10527358/isupportincrementalloading-only-fires-once)

// 以下は初稿時に参考として挙げた記事ですが、現在は "ISupportIncrementalLoading only fires once" の記事を元にした実装を選択しています。あくまで参考としてご覧ください。

* [Implementing the ISupportIncrementalLoading interface in a Windows Store app]
(https://marcominerva.wordpress.com/2013/05/22/implementing-the-isupportincrementalloading-interface-in-a-window-store-app/)

* [Create a ListView with LoadMoreItemsAsync on end of scroll]
(http://stackoverflow.com/questions/26719617/create-a-listview-with-loadmoreitemsasync-on-end-of-scroll)