Chargement incrémentiel : implémentation de ISupportIncrementalLoading

Chargement incrémentiel : implémentation de ISupportIncrementalLoading

ISupportIncrementalLoading

Le framework WinRT fournit une interface permettant d’implémenter le chargement incrémentiel.
Cette interface est toute simple elle n’a que membres.

 
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);
}

Lien vers la documentation MSDN

A quoi ça sert?

L’implémentation de cette interface permet facilement de créer des listes infinie ou des listes qui se chargent au fur et à mesure.
Toutes collections implémentant ISupportIncrementalLoading marchent automatiquement avec les ListView et GridView de WinRT.

Quand se servir de liste incrémentielle?
Quand vous avez des collections à afficher des des GridView/ListView supérieurs à 50 éléments.
En effet ça permet au téléphone de créer que les premiers élèments.
Quand vous voulez gérer les listes infinies…

Utilisation de ma classe abstraite IncrementalLoadingCollection

Pour ne pas à avoir a ré-implémenté la classe IncrementalLoadingCollection pour chaqu’un de mes types chargé de façon incrémentiel je suis fais une petite classe abstraite qui gère tout le système de chargement.

Voici un exemple d’utilisation de ma classe abstraite.

 
    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>();
        }
    }

Implémentation générique

Voici mon implémentation.

 
    /// <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; }
    }

Allez à vos apps!!

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Pin It on Pinterest