Advertisement
Guest User

Untitled

a guest
Feb 25th, 2017
162
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.48 KB | None | 0 0
  1. // ******************************************************************
  2. // Copyright (c) Microsoft. All rights reserved.
  3. // This code is licensed under the MIT License (MIT).
  4. // THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  5. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  6. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  7. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  8. // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  9. // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
  10. // THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
  11. // ******************************************************************
  12.  
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Collections.ObjectModel;
  16. using System.ComponentModel;
  17. using System.Linq;
  18. using System.Threading;
  19. using System.Threading.Tasks;
  20. using Windows.Foundation;
  21. using Windows.UI.Xaml.Data;
  22.  
  23. namespace Microsoft.Toolkit.Uwp
  24. {
  25. /// <summary>
  26. /// This class represents an <see cref="ObservableCollection{IType}"/> whose items can be loaded incrementally.
  27. /// </summary>
  28. /// <typeparam name="TSource">
  29. /// The data source that must be loaded incrementally.
  30. /// </typeparam>
  31. /// <typeparam name="IType">
  32. /// The type of collection items.
  33. /// </typeparam>
  34. /// <seealso cref="IIncrementalSource{TSource}"/>
  35. /// <seealso cref="ISupportIncrementalLoading"/>
  36. public class IncrementalLoadingCollection<TSource, IType> : ObservableCollection<IType>,
  37. ISupportIncrementalLoading
  38. where TSource : IIncrementalSource<IType>
  39. {
  40. /// <summary>
  41. /// Gets or sets an <see cref="Action"/> that is called when a retrieval operation begins.
  42. /// </summary>
  43. public Action OnStartLoading { get; set; }
  44.  
  45. /// <summary>
  46. /// Gets or sets an <see cref="Action"/> that is called when a retrieval operation ends.
  47. /// </summary>
  48. public Action OnEndLoading { get; set; }
  49.  
  50. /// <summary>
  51. /// Gets or sets an <see cref="Action"/> that is called if an error occours during data retrieval. The actual <see cref="Exception"/> is passed as an argument.
  52. /// </summary>
  53. public Action<Exception> OnError { get; set; }
  54.  
  55. /// <summary>
  56. /// Gets a value indicating the source of incremental loading.
  57. /// </summary>
  58. protected TSource Source { get; }
  59.  
  60. /// <summary>
  61. /// Gets a value indicating how many items that must be retrieved for each incremental call.
  62. /// </summary>
  63. protected int ItemsPerPage { get; }
  64.  
  65. /// <summary>
  66. /// Gets or sets a value indicating The zero-based index of the current items page.
  67. /// </summary>
  68. protected int CurrentPageIndex { get; set; }
  69.  
  70. private bool _isLoading;
  71. private bool _hasMoreItems;
  72. private CancellationToken _cancellationToken;
  73. private bool _refreshOnLoad;
  74.  
  75. /// <summary>
  76. /// Gets a value indicating whether new items are being loaded.
  77. /// </summary>
  78. public bool IsLoading
  79. {
  80. get
  81. {
  82. return _isLoading;
  83. }
  84.  
  85. private set
  86. {
  87. if (value != _isLoading)
  88. {
  89. _isLoading = value;
  90. OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsLoading)));
  91.  
  92. if (_isLoading)
  93. {
  94. OnStartLoading?.Invoke();
  95. }
  96. else
  97. {
  98. OnEndLoading?.Invoke();
  99. }
  100. }
  101. }
  102. }
  103.  
  104. /// <summary>
  105. /// Gets a value indicating whether the collection contains more items to retrieve.
  106. /// </summary>
  107. public bool HasMoreItems
  108. {
  109. get
  110. {
  111. if (_cancellationToken.IsCancellationRequested)
  112. {
  113. return false;
  114. }
  115.  
  116. return _hasMoreItems;
  117. }
  118.  
  119. private set
  120. {
  121. if (value != _hasMoreItems)
  122. {
  123. _hasMoreItems = value;
  124. OnPropertyChanged(new PropertyChangedEventArgs(nameof(HasMoreItems)));
  125. }
  126. }
  127. }
  128.  
  129. /// <summary>
  130. /// Initializes a new instance of the <see cref="IncrementalLoadingCollection{TSource, IType}"/> class optionally specifying how many items to load for each data page.
  131. /// </summary>
  132. /// <param name="itemsPerPage">
  133. /// The number of items to retrieve for each call. Default is 20.
  134. /// </param>
  135. /// <param name="onStartLoading">
  136. /// An <see cref="Action"/> that is called when a retrieval operation begins.
  137. /// </param>
  138. /// <param name="onEndLoading">
  139. /// An <see cref="Action"/> that is called when a retrieval operation ends.
  140. /// </param>
  141. /// <param name="onError">
  142. /// An <see cref="Action"/> that is called if an error occours during data retrieval.
  143. /// </param>
  144. /// <seealso cref="IIncrementalSource{TSource}"/>
  145. public IncrementalLoadingCollection(int itemsPerPage = 20, Action onStartLoading = null, Action onEndLoading = null, Action<Exception> onError = null)
  146. : this(Activator.CreateInstance<TSource>(), itemsPerPage, onStartLoading, onEndLoading, onError)
  147. {
  148. }
  149.  
  150. /// <summary>
  151. /// Initializes a new instance of the <see cref="IncrementalLoadingCollection{TSource, IType}"/> class using the specified <see cref="IIncrementalSource{TSource}"/> implementation and, optionally, how many items to load for each data page.
  152. /// </summary>
  153. /// <param name="source">
  154. /// An implementation of the <see cref="IIncrementalSource{TSource}"/> interface that contains the logic to actually load data incrementally.
  155. /// </param>
  156. /// <param name="itemsPerPage">
  157. /// The number of items to retrieve for each call. Default is 20.
  158. /// </param>
  159. /// <param name="onStartLoading">
  160. /// An <see cref="Action"/> that is called when a retrieval operation begins.
  161. /// </param>
  162. /// <param name="onEndLoading">
  163. /// An <see cref="Action"/> that is called when a retrieval operation ends.
  164. /// </param>
  165. /// <param name="onError">
  166. /// An <see cref="Action"/> that is called if an error occours during data retrieval.
  167. /// </param>
  168. /// <seealso cref="IIncrementalSource{TSource}"/>
  169. public IncrementalLoadingCollection(TSource source, int itemsPerPage = 20, Action onStartLoading = null, Action onEndLoading = null, Action<Exception> onError = null)
  170. {
  171. if (source == null)
  172. {
  173. throw new ArgumentNullException(nameof(source));
  174. }
  175.  
  176. Source = source;
  177.  
  178. OnStartLoading = onStartLoading;
  179. OnEndLoading = onEndLoading;
  180. OnError = onError;
  181.  
  182. ItemsPerPage = itemsPerPage;
  183. _hasMoreItems = true;
  184. }
  185.  
  186. /// <summary>
  187. /// Initializes incremental loading from the view.
  188. /// </summary>
  189. /// <param name="count">
  190. /// The number of items to load.
  191. /// </param>
  192. /// <returns>
  193. /// An object of the <see cref="LoadMoreItemsAsync(uint)"/> that specifies how many items have been actually retrieved.
  194. /// </returns>
  195. public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
  196. => LoadMoreItemsAsync(count, new CancellationToken(false)).AsAsyncOperation();
  197.  
  198. /// <summary>
  199. /// Clears the collection and reloads data from the source
  200. /// </summary>
  201. public void Refresh()
  202. {
  203. if (this.IsLoading)
  204. {
  205. _refreshOnLoad = true;
  206. }
  207. else
  208. {
  209. Clear();
  210. CurrentPageIndex = 0;
  211. HasMoreItems = true;
  212. DontAwait(LoadMoreItemsAsync(1));
  213. }
  214. }
  215.  
  216. /// <summary>
  217. /// Actually performs the incremental loading.
  218. /// </summary>
  219. /// <param name="cancellationToken">
  220. /// Used to propagate notification that operation should be canceled.
  221. /// </param>
  222. /// <returns>
  223. /// Returns a collection of <typeparamref name="IType"/>.
  224. /// </returns>
  225. protected virtual async Task<IEnumerable<IType>> LoadDataAsync(CancellationToken cancellationToken)
  226. {
  227. var result = await Source.GetPagedItemsAsync(CurrentPageIndex++, ItemsPerPage, cancellationToken);
  228. return result;
  229. }
  230.  
  231. private async Task<LoadMoreItemsResult> LoadMoreItemsAsync(uint count, CancellationToken cancellationToken)
  232. {
  233. uint resultCount = 0;
  234. _cancellationToken = cancellationToken;
  235.  
  236. try
  237. {
  238. if (!_cancellationToken.IsCancellationRequested)
  239. {
  240. IEnumerable<IType> data = null;
  241. try
  242. {
  243. IsLoading = true;
  244. data = await LoadDataAsync(_cancellationToken);
  245. }
  246. catch (OperationCanceledException)
  247. {
  248. // The operation has been canceled using the Cancellation Token.
  249. }
  250. catch (Exception ex) when (OnError != null)
  251. {
  252. OnError.Invoke(ex);
  253. }
  254.  
  255. if (data != null && data.Any() && !_cancellationToken.IsCancellationRequested)
  256. {
  257. resultCount = (uint)data.Count();
  258.  
  259. foreach (var item in data)
  260. {
  261. Add(item);
  262. }
  263. }
  264. else
  265. {
  266. HasMoreItems = false;
  267. }
  268. }
  269. }
  270. finally
  271. {
  272. IsLoading = false;
  273.  
  274. if (_refreshOnLoad)
  275. {
  276. _refreshOnLoad = false;
  277. Refresh();
  278. }
  279. }
  280.  
  281. return new LoadMoreItemsResult { Count = resultCount };
  282. }
  283.  
  284. private void DontAwait(IAsyncOperation<LoadMoreItemsResult> asyncOperation)
  285. {
  286. // do nothing
  287. }
  288. }
  289. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement