In this article we'll look at how to create two Behaviors for ListView:
One to allow an order to be associated when one of the items in the list is clicked.
One second to implement the infinite scroll
A small difference with WPF:
Behaviors in Xamarin.Forms are slightly different from their WPF counterparts.
They have no AssociatedObject property. The other major difference is that their DataContext is not initialized like that of a normal view.
To solve these little problems, we're going to define a basic class of our behaviors that will add these two behaviors.
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:
In my projects I often want to associate a command with the user click/touch action on a line of my ListView But Xamarin Forms does not provide this mechanism by default.
So I'm going to have to create a Behavior that subscribes to the ItemTapped event and invokes my command.
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); }
}
}
Then to use it is very simple:
<ListView.Behaviors>
<behavior:ListViewOnItemTappedBehavior
Command="{Binding Path=ItemSelectedCommand, Mode=OneWay}" />
</ListView.Behaviors>
I'm going to tell you something.
In almost all my projects I now need a self-page list.
The idea is that when you get to the end of the list you start loading a new page.
To do this we will need two properties:
Property Command which will be invoked when the list needs to load more items.
Property NumberOfItemsBeforeLoadMore Which will be from which end-list index we will start loading a new page of data.
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);
}
}
}
}
As the behaviour precedes the use is also very simple.
Please note that the property allows you to settle from which item
<ListView.Behaviors>
<behavior:ListViewOnItemTappedBehavior
Command="{Binding Path=ItemSelectedCommand, Mode=OneWay}" />
<behavior:InfiniteScrollBehavior
NumberOfItemsBeforeLoadMore="10"
Command="{Binding Path=LoadMoreCommand, Mode=OneWay}" />
</ListView.Behaviors>
To go further:
I advise you to watch the excellent podcast What 's up with Xamarin.Forms ? or the audio format with Thomas Lebrun as a guest.