Xamarin Forms  -  CollectionView Básico - I
 Neste artigo vou apresentar o novo recurso CollectionView da versão 4.0 do Xamarin Forms.

O Xamarin.Forms vem evoluindo e melhorando a cada nova versão, e, com a versão 4.0 temos muitas novidades. Hoje vou apresentar o recurso CollectionView.

Para este artigo estamos usando os seguintes recursos:

Para criar o projeto usado neste artigo no VS 2019 Community veja este link: Criar projeto Xamarin.Forms no VS 2019

A CollectionView é uma view para apresentar listas de dados usando diferentes especificações de layout. O objetivo é fornecer uma alternativa mais flexível e eficiente ao ListView.

Este recurso esta disponível a partir da versão 4.0, e, quando escrevi este artigo, estava em modo experimental. Para poder usar a CollectionView teremos que adicionar o código abaixo ás classes AppDelegate no iOS, e, MainActivity no Android, antes da chamada de Forms.Init:


 global::Xamarin.Forms.Forms.SetFlags("CollectionView_Experimental");

Embora sejam similares, cabe destacar as seguintes diferenças da CollectionView para o ListView:

  1. A CollectionView possui um modelo de layout flexível, que permite que os dados sejam apresentados na vertical ou na horizontal, em uma lista ou grade;

  2. A CollectionView suporta seleção única e múltipla;

  3. A CollectionView não tem conceito de células. Em vez disso, um modelo de dados é usado para definir a aparência de cada item de dados na lista.

  4. A CollectionView usa automaticamente a virtualização fornecida pelos controles nativos subjacentes.

  5. A CollectionView reduz a superfície da API do ListView. Muitas propriedades e eventos do ListView não estão presentes no CollectionView.

  6. A CollectionView não inclui separadores internos.

Uma mudança que cabe destacar é que a CollectionView não possui o ViewCell oque melhora muito o desempenho.

Se você desejar migrar a ListView para a CollectionView pode consultar o roteiro na documentação oficial neste link: https://docs.microsoft.com/pt-br/xamarin/xamarin-forms/user-interface/collectionview/introduction

Como a CollectionView trabalha com dados ?

A CollectionView possui as seguintes propriedades que definem os dados a serem exibidos e a sua aparência:

  1. ItemsSource  : do tipo IEnumerable, especifica a coleção de itens a serem exibidos, e tem um valor padrão de null.
  2. ItemTemplate :  do tipo DataTemplate , especifica o modelo a ser aplicado a cada item na coleção de itens a serem exibidos.

Essas propriedades têm o respaldo dos objetos BindableProperty, o que significa que as propriedades podem ser alvo de vinculações de dados.

Assim, uma CollectionView é preenchida com dados, definindo sua propriedade ItemsSource para qualquer coleção que implemente IEnumerable.

Num exemplo bem simples podemos adicionar itens na CollectionView via código XAML, inicializando a propriedade ItemsSource de um array de strings :

<?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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="XF_CollectinViewBasico.MainPage">
    <CollectionView>
        <CollectionView.ItemsSource>
            <x:Array Type="{x:Type x:String}">
                <x:String>Santos</x:String>
                <x:String>Palmeiras</x:String>
                <x:String>Flamengo</x:String>
                <x:String>Atlético-MG</x:String>
                <x:String>São Paulo</x:String>
                <x:String>Internacional</x:String>
                <x:String>Atlético-PR</x:String>
            </x:Array>
        </CollectionView.ItemsSource>
    </CollectionView>
</ContentPage>

Observe que o elemento x:Array requer um atributo Type que indica o tipo dos itens no array.

Se a CollectionView precisar ser atualizada à medida que os itens são adicionados, removidos ou alterados na coleção subjacente, a coleção subjacente deve ser uma coleção IEnumerable que envia notificações de alteração de propriedade como uma ObservableCollection.

Por padrão, a CollectionView exibe itens em uma lista vertical, conforme mostrado no resultado acima.

Fazendo o Data binding

Uma CollectionView pode ser preenchida com dados usando o data binding ou associação de dados, e associar sua propriedade ItemsSource a uma coleção IEnumerable. No código XAML, isso é feito com a marcação de extensão : Binding

<CollectionView ItemsSource="{Binding Times}" />

Neste exemplo, a propriedade ItemsSource vincula dados para a propriedade Times da viewmodel vinculada.

A aparência de cada item na CollectionView pode ser definida ao configurar a propriedade CollectionView.ItemTemplate para um DataTemplate.

Vejamos um exemplo prático desse comportamento.

Vamos criar um projeto com o nome XF_CollectinViewBasico seguindo as orientações deste artigo: Criar projeto Xamarin.Forms no VS 2019

No projeto criado vamos criar duas pastas : Models e ViewModels.

Na pasta Models vamos definir a classe Time:

    public class Time
    {
        public string Nome { get; set; }
        public string ImagemUrl { get; set; }
        public int Pontos { get; set; }
        public string Detalhes { get; set; }
    }

Na pasta ViewModels vamos criar a classe TimesViewModel com o código a seguir:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Forms;
using XF_CollectinViewBasico.Models;
namespace XF_CollectinViewBasico.ViewModels
{
    public class TimesViewModel : INotifyPropertyChanged
    {
        readonly IList<Time> source;
        Time selectedTime;
        int selectionCount = 1;
        public ObservableCollection<Time> Times { get; private set; }
        public IList<Time> EmptyTimes { get; private set; }
        public Time SelectedTime
        {
            get
            {
                return selectedTime;
            }
            set
            {
                if (selectedTime != value)
                {
                    selectedTime = value;
                }
            }
        }
        ObservableCollection<object> selectedTimes;
        public ObservableCollection<object> SelectedTimes
        {
            get
            {
                return selectedTimes;
            }
            set
            {
                if (selectedTimes != value)
                {
                    selectedTimes = value;
                }
            }
        }
        public string SelectedTimeMessage { get; private set; }
        public ICommand FilterCommand => new Command<string>(FilterItems);
        public ICommand TimeSelectionChangedCommand => new Command(TimeSelectionChanged);
        public TimesViewModel()
        {
            source = new List<Time>();
            CreateTimeCollection();
            selectedTime = Times.Skip(3).FirstOrDefault();
            TimeSelectionChanged();
            SelectedTimes = new ObservableCollection<object>()
            {
                Times[1], Times[3], Times[4]
            };
        }
        void CreateTimeCollection()
        {
            source.Add(new Time
            {
                Nome = "Santos",
                ImagemUrl = "santos64.jpg",
                Pontos = 29,
                Detalhes = "Peixe - O time do Péle e da Vila Belmiro"
            });
            source.Add(new Time
            {
                Nome = "Palmeiras",
                ImagemUrl = "palmeiras.jpg",
                Pontos = 27,
                Detalhes = "Porco - O alviverde paulista"
            });
            source.Add(new Time
            {
                Nome = "Flamengo",
                ImagemUrl = "flamengo64.jpg",
                Pontos = 24,
                Detalhes = "Urubu - O rubro-nego carioca"
            });
            source.Add(new Time
            {
                Nome = "Atlético-MG",
                ImagemUrl = "atleticomg.jpg",
                Pontos = 21,
                Detalhes = "Galo - O alvi-negro mineiro "
            });
            source.Add(new Time
            {
                Nome = "São Paulo",
                ImagemUrl = "saopaulo64.jpg",
                Pontos = 21,
                Detalhes = "São Bento - São Paulo  "
            });
            source.Add(new Time
            {
                Nome = "Internacional",
                ImagemUrl = "internacional.jpg",
                Pontos = 20,
                Detalhes = "Rio Grande do sul  "
            });
            source.Add(new Time
            {
                Nome = "Atlético-PR",
                ImagemUrl = "atleticopr.jpg",
                Pontos = 19,
                Detalhes = "Paraná"
            });
            source.Add(new Time
            {
                Nome = "Corintians",
                ImagemUrl = "corintians64.jpg",
                Pontos = 19,
                Detalhes = "São Jorge - São Paulo"
            });
            source.Add(new Time
            {
                Nome = "Goiás",
                ImagemUrl = "goias.jpg",
                Pontos = 17,
                Detalhes = "Goiás"
            });
            source.Add(new Time
            {
                Nome = "Botafogo",
                ImagemUrl = "botafogo.jpg",
                Pontos = 16,
                Detalhes = "Rio de Janeiro"
            });
            Times = new ObservableCollection<Time>(source);
        }
        void FilterItems(string filter)
        {
            var itensfiltrados = source.Where(time => time.Nome.ToLower().Contains(filter.ToLower())).ToList();
            foreach (var time in source)
            {
                if (!itensfiltrados.Contains(time))
                {
                    Times.Remove(time);
                }
                else
                {
                    if (!Times.Contains(time))
                    {
                        Times.Add(time);
                    }
                }
            }
        }

        void TimeSelectionChanged()
        {
            SelectedTimeMessage = $"Time {selectionCount}: {SelectedTime.Nome}";
            OnPropertyChanged("SelectedTimeMessage");
            selectionCount++;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

No código acima definimos algumas propriedades e definimos os dados que iremos usar para popular a CollectionView.

A seguir, ainda no projeto Android, vamos incluir as imagens dos escudos dos times, listadas a seguir, na pasta /Resources/drawable que iremos usar no projeto.

As imagens podem ser obtidas no site: https://aimore.org/escudos/geral.html

Implementando um layout básico : lista vertical

No arquivo MainPage.xaml vamos incluir o código abaixo que vai usar a CollectionView e definir um leiaute de lista vertical que é o padrão.

Aqui estamos usando um grid e fazendo o data binding com a ViewModel TimesViewModel e as propriedades : Nome, ImagemUrl e Pontos:

<?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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="XF_CollectinViewBasico.MainPage">
    <StackLayout Margin="5">
        <CollectionView ItemsSource="{Binding Times}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid Padding="8">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Image Grid.RowSpan="2" 
                               Source="{Binding ImagemUrl}" 
                               Aspect="AspectFill"
                               HeightRequest="40" 
                               WidthRequest="60" />
                        <Label Grid.Column="1" 
                               Text="{Binding Nome}" 
                               FontSize="Large"
                               TextColor="Blue"
                               FontAttributes="Bold" />
                        <Label Grid.Row="1"
                               Grid.Column="1" 
                               Text="{Binding Pontos}"
                               FontAttributes="Bold" 
                               FontSize="Large"
                               VerticalOptions="End" />
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </StackLayout>
</ContentPage>

Observe que a CollectionView é muito parecida com a ListView possuindo o DataTemplate e a propriedade ItemsSource.

Agora abra o arquivo MainPage.xaml.cs e inclua o código abaixo:

using System.ComponentModel;
using Xamarin.Forms;
using XF_CollectinViewBasico.ViewModels;
namespace XF_CollectinViewBasico
{
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            BindingContext = new TimesViewModel();
        }
    }
}

Estamos criando uma instância de TimesViewModel e fazendo o binding com o BindingContext:

Executando o projeto iremos obter o seguinte resultado:

Na próxima parte do artigo vamos continuar a abordar outros recursos da CollectionView.

Pegue o código do projeto compartilhado aqui :  XF_CollectinViewBasico.zip (sem as referências)

"Pensai nas coisas que são de cima, e não nas que são da terra;"
Colossenses 3:2

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