Xamarin Forms : Créer des behaviors pour la ListView

Xamarin Forms : Créer des behaviors pour la ListView

Dans cet article nous allons voir comment créer deux Behaviors pour la ListView :
– Un pour permettre d’associer une commande lorsque l’un des éléments de la liste est cliqué.
– Un second pour implémenter le scroll infini

Une petite différence avec WPF :

Les Behaviors de Xamarin Forms sont un peu différent de ceux WPF.
Ceux-ci n’ont pas de propriété AssociatedObject.
La seconde grosse différence est que leur DataContext n’est pas initialisé comme une vue normale.
Pour résoudre ces petits problèmes nous allons définir une classe de base à nos behaviors qui va rajouter ces deux comportements.

public abstract class BaseBehavior : Behavior<ListView>
where T : BindableObject
{
   public T AssociatedObject { get; private set; }

   protected override void OnAttachedTo(T bindable)
   {
      base.OnAttachedTo(bindable);

      // Lors de la construction on définit la propriété AssociatedObject
      AssociatedObject = bindable;

      //Si le contexte est != NULL on initiliase le contexte de du Behavior avec celui de l'objet courant
      if (bindable.BindingContext != null)
      {
      BindingContext = bindable.BindingContext;
      }

      bindable.BindingContextChanged += OnBindingContextChanged;
   }

   protected override void OnDetachingFrom(T bindable)
   {
      base.OnDetachingFrom(bindable);
      //On se désabonne
      bindable.BindingContextChanged -= OnBindingContextChanged;

      // Lors de la construction on définit la propriété AssociatedObject
      AssociatedObject = null;
   }

   private void OnBindingContextChanged(object sender, EventArgs e)
   {
      OnBindingContextChanged();
   }

   protected override void OnBindingContextChanged()
   {
      base.OnBindingContextChanged();
      BindingContext = AssociatedObject.BindingContext;
   }
}

OnItemTappedBehavior :

Dans mes projets je veux souvent associer une commande à l’action click/touch utilisateur sur une ligne de ma ListView. Or Xamarin Forms ne fournit pas ce mécanisme par défaut.

Je vais donc devoir créer un Behavior qui s’abonne à l’évènement ItemTapped et qui invoque ma commande.

    public class ListViewOnItemTappedBehavior : BaseBehavior<ListView>
    {
        protected override void OnAttachedTo(ListView bindable)
        {
            base.OnAttachedTo(bindable);
            // Abonnement à ItemTapped
            bindable.ItemTapped += Bindable_ItemTapped;
        }

        protected override void OnDetachingFrom(ListView bindable)
        {
            base.OnDetachingFrom(bindable);
            // Désabonnement à ItemTapped (très important sans ça vous risquez de créer des fuites mémoire).
            bindable.ItemTapped -= Bindable_ItemTapped;
        }

        private void Bindable_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            var cmd = Command;

            if (cmd != null &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; cmd.CanExecute(null))
            {
               cmd.Execute(e.Item);
            }
        }

        public static readonly BindableProperty CommandProperty =
            BindableProperty.CreateAttached(
                nameof(Command),
                typeof(ICommand),
                typeof(ListViewOnItemTappedBehavior),
                null);

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
    }

Puis pour l’utiliser c’est très simple :

       <ListView.Behaviors>
            <behavior:ListViewOnItemTappedBehavior 
                        Command="{Binding Path=ItemSelectedCommand, Mode=OneWay}" />
        </ListView.Behaviors>

InfiniteScrollBehavior:

Dans quasiment tout mes projets j’ai désormais besoin de liste qui s’auto pagine.
L’idée est que lorsque l’on arrive vers la fin de la liste on commence a chargé une nouvelle page de celle-ci.
Pour ce faire nous aurons besoin de deux propriétés :
– La propriété Command qui sera invoqué lorsque la liste aura besoin de charger plus d’éléments.
– La propriété NumberOfItemsBeforeLoadMore qui sera a partir de quel index de fin de liste nous commenceront a charger une nouvelle page de donnée.

    public class InfiniteScrollBehavior : BaseBehavior&amp;amp;amp;amp;lt;ListView&amp;amp;amp;amp;gt;
    {
        public static readonly BindableProperty CommandProperty =
            BindableProperty.Create(
                nameof(Command),
                typeof(ICommand),
                typeof(InfiniteScrollBehavior),
                null);

        public ICommand Command
        {
            get
            {
                return (ICommand)GetValue(CommandProperty);
            }
            set
            {
                SetValue(CommandProperty, value);
            }
        }

        public static readonly BindableProperty NumberOfItemsBeforeLoadMoreProperty =
            BindableProperty.Create(
                nameof(NumberOfItemsBeforeLoadMore),
                typeof(uint),
                typeof(InfiniteScrollBehavior),
                1U,
                BindingMode.OneWay);

        public uint NumberOfItemsBeforeLoadMore
        {
            get
            {
                return (uint)GetValue(NumberOfItemsBeforeLoadMoreProperty);
            }
            set
            {
                SetValue(NumberOfItemsBeforeLoadMoreProperty, value);
            }
        }

        protected override void OnAttachedTo(ListView bindable)
        {
            base.OnAttachedTo(bindable);
            bindable.ItemAppearing += InfiniteListView_ItemAppearing;
        }

        protected override void OnDetachingFrom(ListView bindable)
        {
            base.OnDetachingFrom(bindable);
            bindable.ItemAppearing -= InfiniteListView_ItemAppearing;
        }

        private void InfiniteListView_ItemAppearing(object sender, ItemVisibilityEventArgs e)
        {
            var items = AssociatedObject.ItemsSource as IList;
            if (Command != null &&
                items != null &&
                items.IndexOf(e.Item) >= items.Count - NumberOfItemsBeforeLoadMore)
            {
                if (Command.CanExecute(null))
                {
                    Command.Execute(null);
                }
            }
        }
    }

Comme le behavior précèdent l’utilisation est aussi très simple.
A noter que la propriété permet régler à partir de quel item

        <ListView.Behaviors>
            <behavior:ListViewOnItemTappedBehavior 
                        Command="{Binding Path=ItemSelectedCommand, Mode=OneWay}" />
            <behavior:InfiniteScrollBehavior 
                        NumberOfItemsBeforeLoadMore="10"
                        Command="{Binding Path=LoadMoreCommand, Mode=OneWay}" />
        </ListView.Behaviors>

Pour aller plus loin :

Je vous conseille de regarder l’excellent podcast Quoi de neuf sur Xamarin.Forms ? ou le format audio avec comme invité Thomas Lebrun.

Laisser un commentaire

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

Pin It on Pinterest