Como filtrar coleções e listas por meio de entrada do usuárioFiltering collections and lists through user input

Se sua coleção exibir muitos itens ou estiver fortemente ligada à interação do usuário, a filtragem será um recurso útil de implementar.If your collection displays many items or is heavily tied to user interaction, filtering is a useful feature to implement. A filtragem usando o método descrito neste artigo pode ser implementada na maioria dos controles de coleção, incluindo ListView, GridView e ItemsRepeater.Filtering using the method described in this article can be implemented to most collection controls, including ListView, GridView, and ItemsRepeater. Muitos tipos de entrada do usuário podem ser usados para filtrar uma coleção, como caixas de seleção, botões de opção e controles deslizantes, mas este artigo se concentrará na criação de entrada de usuário baseada em texto e no seu uso para atualizar uma ListView em tempo real, de acordo com a pesquisa do usuário.Many types of user input can be used to filter a collection - such as checkboxes, radio buttons, and sliders - but this article will be focusing on taking text-based user input and using it to update a ListView in real time, according to the user's search.

Observação

Este artigo se concentrará na filtragem com uma ListView.This article will focus on filtering with a ListView. O método de filtragem também pode ser aplicado a outros controles de coleções, como GridView, ItemsRepeater ou TreeView.Please be aware that the filtering method can also be applied to other collections controls such as GridView, ItemsRepeater, or TreeView.

Como configurar a interface do usuário e o XAML para filtragemSetting up the UI and XAML for filtering

Para implementar a filtragem, seu aplicativo deve ter uma ListView exibida junto com uma TextBox ou outro controle que permita a entrada do usuário.To implement filtering, your app should have a ListView should appear alongside a TextBox or other control that allows for user input. O texto que o usuário digita na TextBox será usado como filtro, ou seja, somente os resultados que contiverem sua consulta de entrada/pesquisa de texto serão exibidos.The text that the user types into the TextBox will be used as the filter, i.e. only results containing their text input/search query will appear. À medida que o usuário digita na TextBox, a ListView é constantemente atualizada com resultados filtrados – especificamente, sempre que o texto na TextBox muda, mesmo que em uma única letra, a ListView passará por seus itens e filtrará com esse termo.As the user types into the TextBox, the ListView will constantly update with filtered results - specifically, everytime the text in the TextBox changes, even if by one letter, the ListView will go through its items and filter with that term.

O código a seguir mostra uma interface do usuário com uma ListView simples e seu DataTemplate, juntamente com uma TextBox que acompanha.The code below shows a UI with a simple ListView and its DataTemplate, along with an accompanying TextBox. Neste exemplo, a ListView exibe uma coleção de objetos Person.In this example, the ListView displays a collection of Person objects. Person é uma classe definida no code-behind (não mostrado no exemplo de código abaixo) e cada objeto Person tem as seguintes propriedades: FirstName, LastName e Company.Person is a class defined in the code-behind (not shown in code sample below), and each Person object has the following properties: FirstName, LastName, and Company.

Usando a TextBox, os usuários podem digitar um termo de pesquisa/filtragem para filtrar a lista de objetos Person por sobrenome.Using the TextBox, users can type a search/filtering term to filter the list of Person objects by last name. Observe que a TextBox está associada a um nome específico (FilterByLName) e tem seu próprio evento TextChanged (FilteredLV_LNameChanged).Note that the TextBox is bound to a specific name (FilterByLName) and has its own TextChanged event (FilteredLV_LNameChanged). O nome de ligação nos permite acessar o conteúdo/texto da TextBox no code-behind e o evento TextChanged será acionado sempre que o usuário digitar na caixa de texto, permitindo executar uma operação de filtragem ao receber a entrada do usuário.The bound name allows us to access the TextBox's content/text in the code-behind, and the TextChanged event will fire whenever the user types in the TextBox, allowing us to perform a filtering operation upon recieving user input.

Para a filtragem funcionar, ListView deve ter uma fonte de dados que possa ser manipulada no code-behind, como um ObservableCollection<>.For filtering to work, the ListView must have a data source that can be manipulated in the code-behind, such as an ObservableCollection<>. Nesse caso, a propriedade ItemsSource de ListView é atribuída a um ObservableCollection<Person> no code-behind.In this case, the ListView's ItemsSource property is assigned to an ObservableCollection<Person> in the code-behind.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"></ColumnDefinition>
        <ColumnDefinition Width="1*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
            <RowDefinition Height="400"></RowDefinition>
            <RowDefinition Height="400"></RowDefinition>
    </Grid.RowDefinitions>

    <ListView x:Name="FilteredListView"
                Grid.Column="0"
                Margin="0,0,20,0">

        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:Person">
                <StackPanel>
                    <TextBlock Style="{ThemeResource BaseTextBlockStyle}" Margin="0,5,0,5">
                        <Run Text="{x:Bind FirstName}"></Run>
                        <Run Text="{x:Bind LastName}"></Run>
                    </TextBlock>
                    <TextBlock Style="{ThemeResource BodyTextBlockStyle}" Margin="0,5,0,5" Text="{x:Bind Company}"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>

    </ListView>

    <TextBox x:Name="FilterByLName" Grid.Column="1" Header="Last Name" Width="200"
             HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,0,0,20"
             TextChanged="FilteredLV_LNameChanged"/>
</Grid>

Como filtrar os dadosFiltering the data

As consultas Linq permitem agrupar, ordenar e selecionar determinados itens em uma coleção.Linq queries allow you to group, order, and select certain items in a collection. Para filtrar uma lista, criaremos uma consulta Linq que seleciona apenas os termos que correspondem ao termo da consulta/filtragem de pesquisa inserido pelo usuário digitado na TextBox FilterByLName.For filtering a list, we will be constructing a Linq query that only selects terms that match the user-inputted search query/filtering term, entered in the FilterByLName TextBox. O resultado da consulta pode ser atribuído a um objeto da coleção IEnumerable.The query result can be assigned to an IEnumerable collection object. Assim que tivermos essa coleção, poderemos usá-la para comparar com a lista original, removendo itens que não correspondem e adicionando itens que correspondem (no caso de um backspace).Once we have this collection, we can use it to compare with the original list, removing items that don't match and adding back items that do match (in case of a backspace).

Observação

Para que a ListView se anime da maneira mais intuitiva ao adicionar e subtrair itens, é importante remover e adicionar itens à coleção ItemsSource de ListView em si, em vez de criar uma nova coleção de objetos filtrados e atribuí-la à propriedade ItemsSource de ListView.In order for the ListView to animate in the most intuitive way when adding and subtracting items, it's important to remove and add items to the ListView's ItemsSource collection itself, rather than create a new collection of filtered objects and assign that to the ListView's ItemsSource property.

Para começar, precisaremos inicializar nossa fonte de dados original em uma coleção separada, como uma List<T> ou uma ObservableCollection<T>.To start, we'll need to initialize our original data source in a separate collection, such as a List<T> or ObservableCollection<T>. Neste exemplo, temos um List<Person> chamado People que contém todos os objetos Person mostrados na ListView (o preenchimento/a inicialização dessa Lista não é mostrada no snippet de código abaixo).In this example, we have an List<Person> called People, that holds all of the Person objects shown in the ListView (population/initialization of this List is not shown in the code snippet below). Também precisaremos de uma lista para manter os dados filtrados, que mudarão constantemente sempre que um filtro for aplicado.We'll also need a list to hold the filtered data, which will constantly change every time a filter is applied. Isso será um ObservableCollection<Person> chamado PeopleFiltered e, na inicialização, terá o mesmo conteúdo que People.This will be an ObservableCollection<Person> called PeopleFiltered, and at initialization will have the same contents as People.

O código a seguir executa a operação de filtragem por meio das etapas a seguir, mostradas no código abaixo:The code below performs the filtering operation through the following steps, shown in the code below:

  • Defina a propriedade ItemsSource da ListView como PeopledFiltered.Set the ListView's ItemsSource property to PeopledFiltered.
  • Defina o evento TextChanged, FilteredLV_LNameChanged(), para a TextBox FilterByLName.Define the TextChanged event, FilteredLV_LNameChanged(), for the FilterByLName TextBox. Dentro dessa função, filtre os dados.Inside this function, filter the data.
  • Para filtrar os dados, acesse o termo de consulta/filtragem de pesquisa inserido pelo usuário por meio de FilterByLName.Text.To filter the data, access the user-inputted search query/filtering term through FilterByLName.Text. Use uma consulta LINQ para selecionar os itens em People cujo sobrenome contém o termo FilterByLName.Text e adicione esses itens correspondentes a uma coleção chamada TempFiltered.Use a Linq query to select the items in People whose last name contains the term FilterByLName.Text, and add those matching items into a collection called TempFiltered.
  • Compare a coleção de PeopleFiltered atual com os itens filtrados recentemente no TempFiltered, removendo e adicionando itens de PeopleFiltered quando necessário.Compare the current PeopleFiltered collection with the newly filtered items in TempFiltered, removing and adding items from PeopleFiltered where necessary.
  • À medida que os itens são removidos e adicionados de PeopleFiltered, a ListView será atualizada e animada de acordo.As items are removed and added from PeopleFiltered, the ListView will update and animate accordingly.
using System.Linq;

public MainPage()
{
   // Define People collection to hold all Person objects. 
   // Populate collection - i.e. add Person objects (not shown)
   IList<Person> People = new List<Person>();

   // Create PeopleFiltered collection and copy data from original People collection
   ObservableCollection<Person> PeopleFiltered = new ObservableCollection<Person>(People);

   // Set the ListView's ItemsSource property to the PeopleFiltered collection
   FilteredListView.ItemsSource = PeopleFiltered;

   // ... 
}

private void FilteredLV_LNameChanged(object sender, TextChangedEventArgs e)
{
   /* Perform a Linq query to find all Person objects (from the original People collection)
   that fit the criteria of the filter, save them in a new List called TempFiltered. */
   List<Person> TempFiltered;
   
   /* Make sure all text is case-insensitive when comparing, and make sure 
   the filtered items are in a List object */
   TempFiltered = people.Where(contact => contact.LastName.Contains(FilterByLName.Text, StringComparison.InvariantCultureIgnoreCase)).ToList();
   
   /* Go through TempFiltered and compare it with the current PeopleFiltered collection,
   adding and subtracting items as necessary: */

   // First, remove any Person objects in PeopleFiltered that are not in TempFiltered
   for (int i = PeopleFiltered.Count - 1; i >= 0; i--)
   {
       var item = PeopleFiltered[i];
       if (!TempFiltered.Contains(item))
       {
           PeopleFiltered.Remove(item);
       }
   }

   /* Next, add back any Person objects that are included in TempFiltered and may 
   not currently be in PeopleFiltered (in case of a backspace) */

   foreach (var item in TempFiltered)
   {
       if (!PeopleFiltered.Contains(item))
       {
           PeopleFiltered.Add(item);
       }
   }
}

Agora, à medida que o usuário digitar seus termos de filtragem na TextBox FilterByLName, a ListView será atualizada imediatamente para mostrar apenas as pessoas cujo sobrenome contém o termo de filtragem.Now, as the user types in their filtering terms in the FilterByLName TextBox, the ListView will immediately update to only show the people whose last name contains the filtering term.

Próximas etapasNext steps

Obter o código de exemploGet the sample code