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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
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; 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 :
1
2
3
4
|
<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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
public class InfiniteScrollBehavior : BaseBehavior&amp;amp;amp;lt;ListView&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
1
2
3
4
5
6
7
|
<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 vidéo Quoi de neuf sur Xamarin.Forms ? ou le format audio avec comme invité Thomas Lebrun.
Commentaires