Xamarin Forms -  Consumindo uma Web API ASP .NET Core e Agrupando e Ordenando em um ListView - II


 Neste artigo vamos continuar a criação da aplicação Xamarin Forms que vai consumir uma Web API ASP .NET Core e exibir os itens em uma ListView, realizando o agrupamento e a ordenação dos itens exibidos.

Na primeira parte do artigo apresentamos o backend; a aplicação ASP .NET Core WebAPI que expõe serviços para gerenciar informações de produtos.

Neste artigo vou mostrar o cliente, a aplicação Xamarin Forms, que vai consumir o serviço e exibir as informações dos produtos em uma ListView onde iremos realizar o agrupamento e implementar a busca e ordenação dos itens.

Então vamos à parte prática...

Recursos usados:

Criando o projeto no Visual Studio 2017 Community

Abra o Visual Studio Community 2017 e clique em New Project;

Selecione Visual C#, o template Cross Plataform e a seguir Cross Plataform App(Xamarin.Forms);

Informe o nome XF_ConsumindoWebAPI e clique no botão OK;

A seguir selecione Blank App e marque as plataformas para quais deseja gerar os projetos;

Marque a opção Xamarin.Forms, e, em Code Sharing Strategy, marque .NET Standard, que substitui a opção de projetos PCL a partir do VS 2017 update 15.5.

Definindo o modelo de domínio

Crie uma pasta Models no projeto portátil via menu Project -> New Folder;

Na pasta Models crie a classe Produto que representa o nosso modelo de domínio:

     public class Produto
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Categoria { get; set; }
        public decimal Preco { get; set; }
        public string Foto { get; set; }
    }

Definindo o serviço de acesso a Web API : DataService

Vamos criar uma pasta Services no projeto e nesta pasta criar a classe DataService onde vamos definir um serviço para acessar e gerenciar as informações dos produtos acessando a nossa web api.

A URI que permite acesso aos serviços definidos em nossa Web API é definido pelo local onde você publicou a Web API.

No meu exemplo a URI usada é : http://macoratti-001-site1.etempurl.com/api/produtos"

Eu estou usando o serviço de avaliação do SmarterASPNET : https://www.smarterasp.net/ , e, a uri acima tem um prazo de validade. Por isso aconselho a você a usar a sua URI, podendo mesmo ser a uri local.

Abaixo temos o código da classe DataService:

using System.Net.Http;
using XF_ConsumindoWebAPI.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Text;
namespace XF_ConsumindoWebAPI
{
    public class DataService
    {
        HttpClient client = new HttpClient();
        
        /// <summary>
        /// Obtém os itens de produtos
        /// </summary>
        public async Task<List<Produto>> GetProdutosAsync()
        {
            var response = await client.GetStringAsync("http://macoratti-001-site1.etempurl.com/api/produtos");
            var produtos = JsonConvert.DeserializeObject<List<Produto>>(response);
            return produtos;
        }
        /// <summary>
        /// Adiciona um item de produto
        /// </summary>
        /// <param name="itemToAdd">Item a adicionar.</param>
        public async Task<int> AddProdutoAsync(Produto itemToAdd)
        {
            var data = JsonConvert.SerializeObject(itemToAdd);
            var content = new StringContent(data, Encoding.UTF8, "application/json");
            var response = await client.PostAsync("http://macoratti-001-site1.etempurl.com/api/produtos", content);
            var result = JsonConvert.DeserializeObject<int>(response.Content.ReadAsStringAsync().Result);
            return result;
        }
        /// <summary>
        /// Atualiza um item
        /// </summary>
        /// <param name="itemIndex">indice do Item.</param>
        /// <param name="itemToUpdate">Item a atualizar.</param>
        public async Task<int> UpdateProdutoAsync(int itemIndex, Produto itemToUpdate)
        {
            var data = JsonConvert.SerializeObject(itemToUpdate);
            var content = new StringContent(data, Encoding.UTF8, "application/json");
            var response = await client.PutAsync(string.Concat("http://macoratti-001-site1.etempurl.com/api/produtos", itemIndex), content);
            return JsonConvert.DeserializeObject<int>(response.Content.ReadAsStringAsync().Result);
        }
        /// <summary>
        /// Deleta um item 
        /// </summary>
        /// <returns>O id do item a deletar.</returns>
        /// <param name="itemIndex">indice do item.</param>
        public async Task DeleteProdutoAsync(int itemIndex)
        {
            await client.DeleteAsync(string.Concat("http://macoratti-001-site1.etempurl.com/api/produtos", itemIndex));
        }
    }
}

Definindo a classe para realizar o agrupamento dos dados no ListView

Na pasta Services vamos criar uma classe chamada Agrupar que vai realizar o agrupamento dos produtos pelas categorias em nossa aplicação conforme mostra o código a seguir:

using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace XF_ConsumindoWebAPI.Services
{
    public class Agrupar<TKey, TItem> : ObservableCollection<TItem>
    {
        public TKey Key { get; private set; }
        public Agrupar(TKey key, IEnumerable<TItem> items)
        {
            this.Key = key;
            foreach (var item in items)
                this.Items.Add(item);
        }
    }
}

No código acima o parâmetro Tkey representa o item pelo qual iremos agrupar as informações e TItem representa as informações dos produtos que serão agrupadas.

Essa classe herda de ObservableCollection permitindo assim, adicionar ou remover itens de um grupo existente, ela também define o critério para realizar o agrupamento.

Definindo o código da página MainPage

Abra o arquivo MainPage.xaml e inclua o código abaixo:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XF_ConsumindoWebAPI"
             x:Class="XF_ConsumindoWebAPI.MainPage"
             BackgroundColor="White">

        <StackLayout Orientation="Vertical">
        <Label Text="Lista de Produtos" TextColor="Red" />
        <SearchBar x:Name="sbProcurar" Placeholder="Procurar..." TextChanged="Procurar_TextChanged"/>
        <ListView x:Name="produtoLista" Margin="5" 
                  HasUnevenRows="True" 
                  IsGroupingEnabled="True"
                  GroupDisplayBinding="{Binding Key}"
                  SeparatorVisibility="Default" 
                  SeparatorColor="Black">
                 <ListView.ItemTemplate>
                    <DataTemplate>
                            <ImageCell Text="{Binding Nome}" 
                                       Detail="{Binding Preco, StringFormat='R{0:c}'}" 
                                       ImageSource="{Binding Foto}"
                                       TextColor="Black" DetailColor="Blue"/>
                                       />
                    </DataTemplate>
                </ListView.ItemTemplate>
        </ListView>
        </StackLayout>
</ContentPage>

Para poder agrupar as informações da nossa agenda primeiro vamos temos que habilitar o nosso ListView a aceitar o agrupamento definindo as seguintes propriedades:

Para exibir os produtos usamos um ItemTemplate e a célula ImageCell :

Text - para exibir a primeira linha de texto em fonte maior;
Detail - para exibir uma segunda linha de texto em fonte menor;
ImageSource
- para exibir uma imagem próxima ao texto

Definimos também uma SearchBar que vai permitir procurar os produtos por categoria. Para isso vamos usar o evento TextChanged e definir o código para filtrar pelo nome da categoria.

Vamos agora implementar o código no arquivo MainPage.xaml.cs :

Abra o arquivo MainPage.xaml.cs criado no projeto e a seguir defina o código abaixo:

using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
using XF_ConsumindoWebAPI.Models;
using XF_ConsumindoWebAPI.Services;
namespace XF_ConsumindoWebAPI
{
    public partial class MainPage : ContentPage
    {
        DataService dataService;
        List<Produto> items;     
        public MainPage()
        {
            InitializeComponent();
            dataService = new DataService();
            AtualizaDados();
        }
        private async void AtualizaDados()
        {
            items = await dataService.GetProdutosAsync();
            produtoLista.ItemsSource = Listar();
        }
        private void Procurar_TextChanged(object sender, TextChangedEventArgs e)
        {
            produtoLista.ItemsSource = this.Listar(this.sbProcurar.Text);
        }
        
        public IEnumerable<Agrupar<string, Produto>> Listar(string filtro = "")
        {
            IEnumerable<Produto> produtosFiltrados = this.items;

            if (!string.IsNullOrEmpty(filtro))
                produtosFiltrados = items.Where(l => (l.Nome.ToLower().Contains(filtro.ToLower())) || l.Categoria.ToLower().Contains(filtro.ToLower()));
            return from produto in produtosFiltrados
                   orderby produto.Nome
                   group produto by produto.Categoria into grupos
                   select new Agrupar<string, Produto>(grupos.Key, grupos);
        }       
    }
}

No código estamos criando uma instância da classe DataService e chamando o método AtualizaDados que obtêm as informações de todos os produtos: dataService.GetProdutosAsync(); e chamando o método Listar() realizar o agrupamento e atribui o resultado à propriedade ItemsSource da listview produtoLista.

Executando o projeto iremos obter o seguinte resultado :

Vemos agora as informações da agenda agrupadas em ordem alfabética pela categoria.

Podemos ainda procurar por uma categoria usando a SearchBar.

Pegue o código usado no projeto aqui :   XF_ConsumindoWebAPI.zip (somente o projeto compartilhado)

"Levantai os vossos olhos para os céus, e olhai para a terra em baixo, porque os céus desaparecerão como a fumaça, e a terra se envelhecerá como roupa, e os seus moradores morrerão semelhantemente; porém a minha salvação durará para sempre, e a minha justiça não será abolida."
Isaías 51:6

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti