Curso Windows Phone – LongListSelector: Criando uma lista agrupada por letras

Olá pessoal, tudo bom?

Neste post explico como criar uma lista de carros, por exemplo, agrupada por letras usando o componente LongListSelector do XAML. O trabalho é um pouco árduo, mas vou tentar simplificar as coisas. Vamos a elas:

list-by-letter-longlistselector

1º passo: Criar uma classe em seu projeto para fazer a ordenação e agrupamento dos itens (AlphaKeyGroup.cs)

Essa classe chama-se AlphaKeyGroup<T> e representa a letra do alfabeto e todos os itens que iniciam com ela. Essa classe não é parte do SDK do Windows Phone, por isso, teremos que digitá-la e manualmente adiciona-la ao nosso projeto. Para facilitar, faça o download da classe aqui.


using Microsoft.Phone.Globalization;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ListByLetter
{
  public class AlphaKeyGroup<T> : List<T>
  {
    /// <summary>
    /// O delegate que é usado para obter a informação da chave
    /// </summary>
    /// <param name="item">Um objeto do tipo T</param>
    /// <returns>O valor da chave usado para esse objeto</returns>
    public delegate string GetKeyDelegate(T item);

    /// <summary>
    /// A chave deste grupo
    /// </summary>
    public string Key { get; private set; }

    /// <summary>
    /// Construtor público
    /// </summary>
    /// <param name="key"></param>
    public AlphaKeyGroup(string key)
    {
      Key = key;
    }

    /// <summary>
    /// Cria uma lista de AlphaKeyGroup<T> com chaves "setadas" por um SortedLocaleGrouping
    /// </summary>
    /// <param name="slg"></param>
    /// <returns>Fonte de dados para o LongListSelector</returns>
    private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg)
    {
      List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>();

      foreach(string key in slg.GroupDisplayNames)
      {
        list.Add(new AlphaKeyGroup<T>(key));
      }

      return list;
    }

    /// <summary>
    /// Cria uma lista de AlphaGroup<T> com as chaves "setadas" por um SortedLocaleGrouping
    /// </summary>
    /// <param name="items">Os itens a serem colocados em grupo</param>
    /// <param name="ci">A CultureInfo para ordenar e agrupar</param>
    /// <param name="getKey">Um delegate para pegar a chave de um item</param>
    /// <param name="sort">Irá ordenar os dados se for "true"</param>
    /// <returns>Uma fonte de dados para o LongListSelector</returns>
    public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
    {
      SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
      List<AlphaKeyGroup<T>> list = CreateGroups(slg);

      foreach (T item in items)
      {
        int index = 0;
        if (slg.SupportsPhonetics)
        {
          //index = slg.GetGroupIndex(getKey(Yomiof(item)));
        }
        else
        {
          index = slg.GetGroupIndex(getKey(item));
        }
        if (index >= 0 &amp;&amp; index < list.Count)
        {
          list[index].Add(item);
        }
      }
      if (sort)
      {
        foreach(AlphaKeyGroup<T> group in list)
        {
          group.Sort((c0, c1) => { return ci.CompareInfo.Compare(getKey(c0), getKey(c1)); });
        }
      }
      return list;
    }
  }
}

2º passo: Preparar a classe que representará os dados para teste

Nesse passo declaramos uma classe Car para posteriormente representarmos uma lista de carros a serem exibidos através do LongListSelector. Vejamos a classe Car.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ListByLetter
{
  public class Car
  {
    public string Name { get; set; }
    public int Year { get; set; }
    public string Brand { get; set; }
  }
}

Agora, para popular nossa lista de carros vamos utilizar o próprio construtor da MainPage.xaml.cs. Veja o código abaixo:


public MainPage()
{
  InitializeComponent();

  this.Loaded += MainPage_Loaded;
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
  List<Car> cars = new List<Car>();
  cars.Add(new Car { Name = "Fusca", Year = 1972, Brand = "VW" });
  cars.Add(new Car { Name = "Frontier", Year = 2009, Brand = "Nissan" });
  cars.Add(new Car { Name = "C3", Year = 2012, Brand = "Citroen" });
  cars.Add(new Car { Name = "Corolla", Year = 2009, Brand = "Toyota" });
  cars.Add(new Car { Name = "Uno", Year = 2002, Brand = "Fiat" });
  cars.Add(new Car { Name = "Ford Modelo A", Year = 1929, Brand = "Ford" });
  cars.Add(new Car { Name = "Fusca", Year = 1982, Brand = "VW" });
  cars.Add(new Car { Name = "Symbol", Year = 2009, Brand = "Renault" });
  cars.Add(new Car { Name = "Palio", Year = 2007, Brand = "Fiat" });
  cars.Add(new Car { Name = "Civic", Year = 2007, Brand = "Honda" });
  cars.Add(new Car { Name = "Camaro", Year = 2014, Brand = "Chevrolet" });
  cars.Add(new Car { Name = "Monza", Year = 1985, Brand = "Chevrolet" });

  List<AlphaKeyGroup<Car>> list = AlphaKeyGroup<Car>.CreateGroups(cars, Thread.CurrentThread.CurrentUICulture, c => c.Brand, true);

  llsCars.ItemsSource = list; //Ver comentário abaixo
}

O objeto llsCars corresponde ao LongListSelector que será declarado mais à frente…

3° passo: Definição do LongListSelector e seus respectivos recursos – XAML

Em relação ao desenvolvimento XAML temos que declarar o LongListSelector e mais alguns Resources para a página onde o mesmo será exibido, em nosso caso, na própria MainPage.xaml.

Teremos que definir um DataTemplate para quando a lista agrupada por letras estiver em modo de exibição de seus itens e um estilo para quando o LongListSelector estiver em modo de seleção de suas opções. Devemos reparar que na definição do estilo estaremos usando dois converters chamados de JumpListItemBackgroundConverter e JumpListItemForegroundConverter, sendo que os mesmos são usados para identificar os grupos de letras que não possuem itens, ou seja, devem aparecer em cinza para que o usuário já perceba que ali não há itens a serem exibidos. Caso contrário, os grupos de letras serão exibidos em branco com o fundo de acordo com o phone accent color.

Ainda em relação aos recursos da página, teremos outro DataTemplate que irá dizer para para o LongListSelector a forma como os dados dos carros deverão ser apresentados. Vamos às definições:

...
<!-- Definição dos recursos para a página -->
<phone:PhoneApplicationPage.Resources>
  <!-- Definição do template para apresentação da lista em modo de exibição -->
  <DataTemplate x:Key="CarsGroupHeaderTemplate">
    <Border Background="Transparent" Padding="5">
      <Border Background="{StaticResource PhoneAccentBrush}"
              BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2"
              Width="62" Height="62" Margin="0,0,18,0" HorizontalAlignment="Left">
        <TextBlock Text="{Binding Key}"
              Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48"
              Padding="6" FontFamily="{StaticResource PhoneFontFamilySemiLight}"
              HorizontalAlignment="Left" VerticalAlignment="Center" />
      </Border>
    </Border>
  </DataTemplate>

  <!-- Definição dos converters para os grupos de letras que não possuem itens -->
  <phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter" />
  <phone:JumpListItemForegroundConverter x:Key="ForegroundConverter" />

  <!-- Definição do estilo para quando o LongListSelector estiver em modo de pesquisa -->
  <Style x:Key="CarsJumpListStyle" TargetType="phone:LongListSelector">
    <Setter Property="GridCellSize" Value="113,113"/>
    <Setter Property="LayoutMode" Value="Grid"/>
    <Setter Property="ItemTemplate">
      <Setter.Value>
        <DataTemplate>
          <Border Background="{Binding Converter={StaticResource BackgroundConverter}}" Width="113" Height="113" Margin="6">
            <TextBlock Text="{Binding Key}" FontFamily="{StaticResource PhoneFontFamilySemiBold}"
              FontSize="48" Padding="6"
              Foreground="{Binding Converter={StaticResource ForegroundConverter}}"
              VerticalAlignment="Center" />
          </Border>
        </DataTemplate>
      </Setter.Value>
    </Setter>
  </Style>

  <!-- Definição do template para exibição da lista de carros -->
  <DataTemplate x:Key="CarsItemTemplate">
    <StackPanel Orientation="Horizontal" Tap="StackPanel_Tap">
      <TextBlock Text="{Binding Brand}" FontSize="30" />
      <TextBlock Text="{Binding Name}" Margin="10,0,0,0" FontSize="30" />
      <TextBlock Text="{Binding Year}" Margin="10,0,0,0" FontSize="30" />
    </StackPanel>
  </DataTemplate>
</phone:PhoneApplicationPage.Resources>
...

Por fim, devemos agora declarar o próprio LongListSelector e indicar a ele todos os recursos a serem utilizados. Vejamos:

...
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <phone:LongListSelector x:Name="llsCars"
         IsGroupingEnabled="True"
         HideEmptyGroups="True"
         GroupHeaderTemplate="{StaticResource CarsGroupHeaderTemplate}"
         ItemTemplate="{StaticResource CarsItemTemplate}"
         JumpListStyle="{StaticResource CarsJumpListStyle}" />
</Grid>
...

Agora basta executar e visualizar o resultado. Fácil, não? 🙂

Grande abraço !
Eduardo Henrique Rizo

Post Relacionado: 

[twitter-follow screen_name=’eduardorizo’ show_count=’yes’]

3 comentários em “Curso Windows Phone – LongListSelector: Criando uma lista agrupada por letras”

  1. Pingback: Free: Curso Windows Phone – Vários tópicos | Blog do Eduardo H. Rizo

Deixe um comentário