SupportIncrementalLoading
The WinRT framework provides an interface to implement incremental loading.
This interface is very simple. It only has members.
public interface ISupportIncrementalLoading
{
//Si false l'objet n'a plus "rien" à charger en plus
bool HasMoreItems { get; }
//Méthode appelé automatiquement par l'UI.
IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count);
}
Link to the MSDN documentation
What's the point?
The implementation of this interface makes it easy to create infinite lists or lists that load over time.
All collections implementing ISupportIncrementalLoading automatically work with WinRT's ListView and GridView.
When to use an incremental list?
When you have more than 50 GridView/ListView collections to view.
It actually allows the phone to create only the first elements.
When you want to handle the endless lists...
Using my abstract class IncrementalLoadingCollection
In order not to have re-implemented the IncrementalLoadingCollection class for each of my incrementally loaded types I am doing a small abstract class that manages the entire loading system.
Here is an example of using my abstract class.
public class UserCollection : IncrementalLoadingCollection<User>
{
protected override async Task<LoadResult<Channel>> InternalLoadMoreItems(uint pageSize, uint currentPage)
{
var rep = //TODO GETDATA
if (!rep.HasError)
return new LoadResult<Channel>(rep.Users, rep.Total);
return new LoadResult<User>();
}
}
General implementation
This is my implementation.
/// <summary>
/// Collection qui supporte le chargement incrémentiel
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class IncrementalLoadingCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
#region Fields
DateTime _dateLastUpdate = DateTime.MinValue;
const uint _MAX_SIZE = 100;
const uint _PAGE_SIZE = 10;
bool _hasMoreItems;
bool _hasItem;
bool _isActive;
uint _currentPage;
#endregion
/// <summary>
/// Taille d'une page
/// </summary>
protected virtual uint PageSize
{
get
{
return _PAGE_SIZE;
}
}
/// <summary>
/// Taille maximum de la collection
/// </summary>
protected virtual uint MaxSize
{
get
{
return _MAX_SIZE;
}
}
/// <summary>
/// Obtient si la collection est active (en cours de chargement)
/// </summary>
public bool IsActive
{
get
{
return _isActive;
}
private set
{
if (_isActive != value)
{
_isActive = value;
OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("IsActive"));
FireHasItemAndLoaded();
}
}
}
protected IncrementalLoadingCollection()
{
_hasMoreItems = true;
}
/// <summary>
/// Obtient si la collection à d'autres items à charger
/// </summary>
public bool HasMoreItems
{
get
{
return _hasMoreItems;
}
}
/// <summary>
/// Notifie que HasItems changé
/// </summary>
void FireHasItemChanged()
{
bool oldHasItem = _hasItem;
bool newHasItem = Items.Any();
if (oldHasItem != newHasItem)
{
_hasItem = newHasItem;
base.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("HasItem"));
FireHasItemAndLoaded();
}
}
void FireHasItemAndLoaded()
{
base.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("HasItemAndLoaded"));
}
/// <summary>
/// Obtient si la collection à des Items
/// </summary>
public bool HasItem
{
get
{
return _hasItem;
}
}
public bool HasItemAndLoaded
{
get
{
return _hasItem && !IsActive;
}
}
/// <summary>
/// Rafraichit la collection
/// </summary>
/// <returns></returns>
public async Task Refresh()
{
Clear();
_hasMoreItems = true;
await LoadMoreItems(PageSize);
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
return AsyncInfo.Run(ct => LoadMoreItemsAsync(ct, count));
}
async Task<LoadMoreItemsResult> LoadMoreItemsAsync(CancellationToken ct, uint count)
{
Debug.WriteLine("Count {0}", count);
if (IsActive)
return new LoadMoreItemsResult() { Count = 0 };
if (DateTime.Now.Subtract(_dateLastUpdate).TotalSeconds < 1)
return new LoadMoreItemsResult() { Count = 0 };
Debug.WriteLine("Loading {0}", count);
uint countAfterLoad = await LoadMoreItems(count);
return new LoadMoreItemsResult
{
Count = countAfterLoad
};
}
protected abstract Task<LoadResult<T>> InternalLoadMoreItems(uint pageSize, uint currentPage);
async Task<uint> LoadMoreItems(uint count)
{
if (IsActive)
return 0;
IsActive = true;
uint result = 0;
try
{
_dateLastUpdate = DateTime.Now;
var nbItems = await InternalLoadMoreItems(PageSize, _currentPage);
_dateLastUpdate = DateTime.Now;
if (nbItems.NbAdded > 0)
{
CoreDispatcher dispatcher = Window.Current.Dispatcher;
await dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
foreach (var item in nbItems.Items)
this.Add(item);
});
}
_currentPage++;
if (Count > (int)MaxSize)
_hasMoreItems = false;
if (Count >= nbItems.NbTotal)
_hasMoreItems = false;
if (nbItems.NbAdded == 0)
_hasMoreItems = false;
result = nbItems.NbAdded;
}
catch (Exception)
{
if (_currentPage == 0)
_hasMoreItems = false;
result = 0;
}
IsActive = false;
return result;
}
protected override void ClearItems()
{
_currentPage = 0;
_hasMoreItems = true;
base.ClearItems();
FireHasItemChanged();
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
FireHasItemChanged();
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
FireHasItemChanged();
}
}
/// <summary>
/// Résultat d'un chargement incrémentiel.
/// </summary>
/// <typeparam name="T"></typeparam>
public class LoadResult<T>
{
public LoadResult(IEnumerable<T> items, int nbTotal)
{
InitializeItems(items);
NbTotal = nbTotal;
}
public LoadResult(IEnumerable<T> items = null)
{
InitializeItems(items);
NbTotal = int.MaxValue;
}
void InitializeItems(IEnumerable<T> items)
{
if (items == null)
{
NbAdded = 0;
}
else
{
Items = items;
NbAdded = (uint)items.Count();
}
}
public IEnumerable<T> Items { get; private set; }
public uint NbAdded { get; private set; }
public int NbTotal { get; private set; }
}
Go to your apps!!