Como filtrar coleções e listas por meio de entrada do usuário

Se sua coleção exibir muitos itens ou estiver fortemente ligada à interação do usuário, a filtragem será um recurso útil de implementar. A filtragem usando o método descrito neste artigo pode ser implementada com a maioria dos controles de coleção, incluindo ListView, GridView e ItemsView. 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 demonstra a criação de entrada de usuário baseada em texto e o seu uso para atualizar uma ListView em tempo real, de acordo com a pesquisa do usuário.

Como configurar a interface do usuário para filtragem

Para implementar a filtragem de texto, seu aplicativo precisará de um ListView e um TextBox ou outro controle que permita a entrada do usuário. O texto que o usuário digita no controle TextBox é usado como o filtro; ou seja, somente os resultados que contêm a entrada de texto do usuário aparecerão no ListView. À medida que o usuário digita no TextBox, o ListView é constantemente atualizado com os resultados filtrados.

Observação

Este artigo demonstra a filtragem com um ListView. No entanto, a filtragem que é demonstrada também pode ser aplicada a outros controles de coleções, como GridView, ItemsView ou TreeView.

O XAML a seguir mostra uma interface do usuário com um ListView simples junto com um TextBox que acompanha. Neste exemplo, o ListView exibe uma coleção de objetos Contact. Contact é uma classe definida no code-behind e cada objeto Contact tem as seguintes propriedades: FirstName, LastName e Company.

O usuário pode digitar um termo de filtragem no TextBox para filtrar a lista de objetos Contact por sobrenome. O TextBox tem seu conjunto de atributos x:Name (FilterByLastName) para que você possa acessar a propriedade Text do TextBox no code-behind. Você também manipula o evento TextChanged (OnFilterChanged). O evento TextChanged ocorre sempre que o usuário digita no TextBox, permitindo que você execute uma operação de filtragem ao receber a entrada de usuário.

Para a filtragem funcionar, ListView deve ter uma fonte de dados que possa ser manipulada no code-behind, como um ObservableCollection<T>. Nesse caso, a propriedade ItemsSource do ListView é atribuída a um ObservableCollection<Contact> no code-behind.

Dica

Esta é uma versão simplificada do exemplo na página ListView do aplicativo WinUI Gallery. Use o aplicativo WinUI Gallery para executar e exibir o código completo, incluindo o DataTemplate e a classe Contact do ListView.

<Grid>
    <StackPanel Width="300" Margin="24"
                HorizontalAlignment="Left">
        <TextBox x:Name="FilterByLastName"
                 Header="Filter by Last Name"
                 TextChanged="OnFilterChanged"/>
        <ListView x:Name="FilteredListView"
             ItemTemplate="{StaticResource ContactListViewTemplate}"/>
    </StackPanel>
</Grid>

Filtragem de dados

As consultas Linq permitem agrupar, ordenar e selecionar determinados itens em uma coleção. Para filtrar uma lista, você constrói uma consulta Linq que seleciona apenas itens que correspondem ao termo de filtragem inserido pelo usuário, digitado no TextBox FilterByLastName. O resultado da consulta pode ser atribuído a um objeto da coleção IEnumerable<T> . Assim que você tiver essa coleção, poderá usá-la para comparar com a lista original, removendo itens que não correspondem e adicionando itens que correspondem (no caso de um backspace).

Observação

Para que o ListView se anime da maneira mais intuitiva ao adicionar e remover itens, é importante adicionar e remover itens na coleção ItemsSource de ListView em si, em vez de criar uma coleção de objetos filtrados e atribuí-la à propriedade ItemsSource de ListView.

Para começar, você precisará inicializar sua fonte de dados original em uma coleção separada, como uma List<T>. Neste exemplo, você tem uma List<Contact> chamada allContacts que contém todos os objetos Contact que podem ser mostrados no ListView.

Você também precisará de uma coleção para manter os dados filtrados, que mudarão constantemente sempre que um filtro for aplicado. Para isso, você usará um ObservableCollection<T> para que o ListView seja notificado para atualizar sempre que a coleção for alterada. Neste exemplo, ele é um ObservableCollection<Person> chamado contactsFiltered, e é o ItemsSource para o ListView. Na inicialização, ele terá o mesmo conteúdo que allContacts.

A operação de filtragem é executada por meio destas etapas, mostradas no código a seguir:

  • Defina a propriedade ItemsSource do ListView como contactsFiltered.
  • Manipule o evento TextChanged (OnFilterChanged) para o TextBox FilterByLastName. Dentro dessa função de manipulador de evento, filtre os dados.
  • Para filtrar os dados, acesse o termo de filtragem inserido pelo usuário por meio da propriedade FilterByLastName.Text. Use uma consulta Linq para selecionar os itens em allContacts cujo sobrenome contém o termo em FilterByLastName.Text, e adicione esses itens correspondentes a uma coleção chamada filtered.
  • Compare a coleção contactsFiltered com os itens filtrados recentemente no filtered, removendo e adicionando itens em contactsFiltered quando necessário para fazer com que ele corresponda a filtered.
  • À medida que os itens são removidos e adicionados a contactsFiltered, o ListView é atualizado e animado de acordo.
using System.Linq;

public sealed partial class MainPage : Page
{
// Define Contact collection to hold all Contact objects.
IList<Contact> allContacts = new List<Contact>();
// Define an ObservableCollection<Contact> object to serve as the ListView's
// ItemsSource. This collection will get updated after the filters are used:
ObservableCollection<Contact> contactsFiltered;

public MainPage()
{
   this.InitializeComponent();

   // Populate allContacts collection.
   allContacts.Add(new Contact("Kendall", "Collins", "Adatum Corporation"));
   allContacts.Add(new Contact("Victoria", "Burke", "Bellows College"));
   allContacts.Add(new Contact("Preston", "Morales", "Margie's Travel"));
   allContacts.Add(new Contact("Miguel", "Reyes", "Tailspin Toys"));

   // Populate contactsFiltered with all Contact objects (in this case,
   // allContacts holds all of our Contact objects so we copy them into
   // contactsFiltered). Set this newly populated collection as the
   // ItemsSource for the ListView.
   contactsFiltered = new ObservableCollection<Contact>(allContacts);
   Filtereditemscontrol.itemssource = contactsFiltered;
}

// Whenever text changes in the filtering text box, this function is called:
private void OnFilterChanged(object sender, TextChangedEventArgs args)
{
   // This is a Linq query that selects only items that return true after
   // being passed through the Filter function, and adds all of those
   // selected items to filtered.
   var filtered = allContacts.Where(contact => Filter(contact));
   Remove_NonMatching(filtered);
   AddBack_Contacts(filtered);
}

// The following functions are called inside OnFilterChanged:

// When the text in any filter is changed, perform a check on each item in
// the original contact list to see if the item should be displayed. If the
// item passes the check, the function returns true and the item is added to
// the filtered list. Make sure all text is case-insensitive when comparing.
private bool Filter(Contact contact)
{
   return contact.LastName.Contains
       (FilterByLastName.Text, StringComparison.InvariantCultureIgnoreCase);
}

// These functions go through the current list being displayed
// (contactsFiltered), and remove any items not in the filtered collection
// (any items that don't belong), or add back any items from the original
// allContacts list that are now supposed to be displayed. (Adding/removing
// the items ensures the list view uses the desired add/remove animations.)

private void Remove_NonMatching(IEnumerable<Contact> filteredData)
{
   for (int i = contactsFiltered.Count - 1; i >= 0; i--)
   {
       var item = contactsFiltered[i];
       // If contact is not in the filtered argument list,
       // remove it from the ListView's source.
       if (!filteredData.Contains(item))
       {
           contactsFiltered.Remove(item);
       }
   }
}

private void AddBack_Contacts(IEnumerable<Contact> filteredData)
{
   foreach (var item in filteredData)
   {
       // If the item in the filtered list is not currently in
       // the ListView's source collection, add it back in.
       if (!contactsFiltered.Contains(item))
       {
           contactsFiltered.Add(item);
       }
   }
}
}

Agora, à medida que o usuário digitar sua cadeia de caracteres de filtragem no TextBox FilterByLastName, o ListView será atualizado imediatamente para mostrar apenas as pessoas cujo sobrenome contém a cadeia de caracteres de filtragem.

Obter o código de exemplo

O aplicativo Galeria da WinUI 3 inclui exemplos interativos da maioria dos controles, recursos e funcionalidades da WinUI 3. Obtenha o aplicativo na Microsoft Store ou o código-fonte no GitHub

Para UWP: o aplicativo WinUI 2 Gallery inclui exemplos interativos da maioria dos controles, recursos e funcionalidades do WinUI 2. Obtenha o aplicativo na Microsoft Store ou o código-fonte no GitHub.