Trabalhar com listas no Xamarin Forms nem sempre é uma tarefa simples. Precisamos pensar na exibição de dados, consumos de internet e e tudo mais. Neste post vamos ver uma estratégia para atualizar o conteúdo da ListView sob demanda, conforme o usuário estiver vendo, o Infinite Scroll, utilizando o Prism, sem necessidade de plugin, sem necessidade de criar um novo Custom Control o Custom Renderer.
Criando o evento na ListView
Se você não conhece o Prism, eu já falei dele anteriormente neste post.
Vou partir do princípio que você já possui o Prism instalado no projeto.
O primeiro passo é construir a lista no XAML:
<ListView HasUnevenRows="true"
ItemsSource="{Binding Produtos}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Nome}">
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Adicionando o Prism EventToCommandBehaviors
Até aqui não há nada de novo do que já estamos acostumados a fazer com ListViews no Xamarin.
Para conseguir fazer o infinite scroll de modo desacoplado e testável, a grande sacada do Prism é permitir que nós consigamos usar os eventos na View Model e não no code behind.
Para isto, o primeiro passo é adicionar o namespace dos Behaviors do Prism na sua Página:
xmlns:behaviors="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
Depois de adicionar os Behaviors do Prism, podemos utilizar o EventToCommandBehavior. A classe EventToCommandBehavior fornece uma maneira conveniente de, em XAML, “ligar” eventos ao ICommand da View Model utilizando o padrão MVVM para evitar o que seja necessário escrever códigos no Code Behind.
<ListView.Behaviors>
<behaviors:EventToCommandBehavior Command="{Binding ItemAppearingCommand}";
EventArgsParameterPath="Item"
EventName="ItemAppearing"/>
</ListView.Behaviors>
No código acima nós adicionamos a propriedade Behaviors da ListView, nativo do Xamarin, e dentro dela, adicionamos os Behaviors do Prism. Dos Behaviors do Prism selecionamos o EventToCommandBehavior, quer fará toda a mágica acontecer. Ele precisa de ao menos 3 parâmetros para ser executado: Command (que será o Command executado na View Model quando o evento acontecer), EventArgsParameterPath (que será os argumentos enviados ao Command) e EventName (que é o nome do evento que queremos escutar).
Em EventArgsParameterPath estamos passando o texto Item. Isto é suficiente para o Prism entender que estamos querendo obter o item atual que está sendo exibido neste momento na tela. Ele se encarregará de obte-lo da tela e envia-lo para o Command.
Veja como ficou a página inteira após adicionar os códigos acima:
<ContentPage x:Class=&quot;MeuApp.Views.ProdutosPage>
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:behaviors="clr-namespace:Prism.Behaviors;assembly=Prism.Forms">
<ContentPage.Content>
<StackLayout>
<ListView HasUnevenRows="true"
ItemsSource="{Binding Produtos}">
<ListView.Behaviors>
<behaviors:EventToCommandBehavior Command="{Binding ItemAppearingCommand}"
EventArgsParameterPath="Item"
EventName="ItemAppearing" />
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Nome}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Obtendo o último elemento da lista e adicionando mais items
Pronto, agora tudo o que precisamos fazer é adicionar um Command na nossa ViewModel que receba o item como parâmetro e adicione mais items a lista Produtos:
public ICommand ItemAppearingCommand { get; set; }
E então instancia-lo no construtor da ViewModel:
ItemAppearingCommand = new Command((item) =>
{
if(Produtos.Last().Id == ((Produto)item).Id)
{
AtualizarListaDeProdutos();
}
});
No Command acima estamos verificando se o último item da nossa lista de Produtos é o mesmo item que foi enviado pelo EventToCommandBehavior da view para view model. Perceba que pedimos o parâmetro item na instanciação do Command. Ele receberá o valor do Item que pedimos na View na propriedade EventArgsParameterPath.
Veja a classe completa:
using MvvmHelpers;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Services;
using MeuApp.Models;
using MeuApp.Services;
using System.Windows.Input;
using Xamarin.Forms;
namespace MeuApp.ViewModels
{
public class ProdutosPageViewModel : BindableBase, INavigatingAware
{
public readonly IProdutoService _produtoService;
public ObservableRangeCollection<Produto> Produtos { get; set; }
public ICommand ItemAppearingCommand { get; set; }
public ProdutosPageViewModel(IProdutoService produtoService)
{
_produtoService = produtoService;
Produtos = new ObservableRangeCollection<Produto>();
ItemAppearingCommand = new Command((item) =>
{
if(Produtos[Produtos.Count -1].Id == ((Produto)item).Id)
{
AtualizarListaDeProdutos();
}
});
}
public void OnNavigatedFrom(INavigationParameters parameters)
{
}
public void OnNavigatingTo(INavigationParameters parameters)
{
AtualizarListaDeProdutos();
}
private void AtualizarListaDeProdutos() => Produtos.AddRange(_produtoService.ObterProdutos());
}
}
Feedback para o usuário
Pronto! Se você quiser, pode também adicionar um loading no rodapé da ListView para indicar ao usuário que está buscando mais items:
<ListView.Footer>
<StackLayout IsVisible="{Binding BuscandoMaisProdutos}" Margin="0, 10, 0, 10">
<ActivityIndicator IsRunning="true"
WidthRequest="40"
HeightRequest="40"/>
</StackLayout>
</ListView.Footer>
E então setar a flag BuscandoMaisProdutos como true antes de chamarmos o serviço que irá buscar os produtos e como false quando os produtos retornarem:
if(Produtos[Produtos.Count -1].ProdutoId == ((Produto)item).ProdutoId)
{
Device.BeginInvokeOnMainThread(()=> BuscandoMaisProdutos = true);
AtualizarListaDeProdutos();
Device.BeginInvokeOnMainThread(() => BuscandoMaisProdutos = false);
}
Estamos utilizando o Device.BeginInvokeOnMainThread para garantir que a atualização da propriedade e a notificação da alteração ocorreram na Main Thread e a view será notificada desta atualização.
Conclusão
O EventToCommandBehavior pode ser usado para tratar diversos eventos na View Model, isto facilita bastante a testabilidade do sistema, além de reduzir a complexidade.
Você pode ler mais sobre o EventToCommandBehavior aqui.
Imagem utilizada no post PixaBay
Comentários