[Desenvolvimento] LongListSelector: Criando uma lista agrupada por letras

6

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
 
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 && 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
...
<!-- 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:

1
2
3
4
5
6
7
8
9
10
...
<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 (@eduardorizo)

Post Relacionado: 

Fonte: Blog do Eduardo H. Rizo

Share.

About Author

Bacharel em Ciência da Computação pela FIPP/Unoeste, Pós-Graduado em Desenvolvimento de Sistemas Web, Segurança da Informação e Avaliação do Ensino e da Aprendizagem, Professor universitário e responsável pelo setor de desenvolvimento de sistemas web da Universidade do Oeste Paulista. MCP e MCPS pela Microsoft e nas horas vagas dedica um pouco do seu tempo escrevendo posts técnicos sobre desenvolvimento de sistemas web, windows phone, gerenciamento de servidores e outros assuntos para a comunidade.

  • OI, ao tentar fazer no visual studio 2012 está dando o erro.:
    “Event handler ‘StackPanel_Tap’ not found on class ‘DataBoundApp1.MainPage'”

  • Diego

    Estou com vontade de aprender a desenvolver aplicativos. Onde posso encontrar algum livro ou site que ensine os passos iniciais??? Obrigado desde já

  • Eron Iury

    Perfeito! Poderia dar uma pincelada em como fazer para customizar o visual das listas, adicionar imagens e etc..

  • Agnaldo Santana da Silva

    Alguém ai sabe como faço pra que meu dvd leia meus arquivos de musica do lumia?

  • Henrique Santana

    Brother sabe como usar a câmera pra identificar caracteres. Tipo a opção de ver uma foto e tirar as palavras e traduzir, assim como ja tem.

  • David Matheus Santos Sousa