Filtro di raccolte ed elenchi tramite input utenteFiltering collections and lists through user input

Se la tua raccolta contiene molti elementi o è strettamente legata all'interazione utente, è utile implementare la funzionalità di filtro.If your collection displays many items or is heavily tied to user interaction, filtering is a useful feature to implement. È possibile implementare tale funzionalità con il metodo descritto in questo articolo per la maggior parte dei controlli delle raccolte, inclusi ListView, GridView e ItemsRepeater.Filtering using the method described in this article can be implemented to most collection controls, including ListView, GridView, and ItemsRepeater. Per filtrare una raccolta, è possibile usare molti tipi di input utente, ad esempio caselle di controllo, pulsanti di opzione e dispositivi di scorrimento. Questo articolo, tuttavia, è incentrato sull'acquisizione di input utente basato su testo e sul relativo uso per aggiornare un controllo ListView in tempo reale, in base alla ricerca eseguita dall'utente.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.

Nota

Questo articolo è incentrato sull'operazione filtro con un controllo ListView.This article will focus on filtering with a ListView. Tieni presente che il metodo di filtro può essere applicato anche ad altri controlli delle raccolte, ad esempio GridView, ItemsRepeater o TreeView.Please be aware that the filtering method can also be applied to other collections controls such as GridView, ItemsRepeater, or TreeView.

Impostazione dell'interfaccia utente e di XAML per il filtroSetting up the UI and XAML for filtering

Per implementare il filtro, l'app deve includere un controllo ListView accanto a una casella di testo o un controllo di altro tipo che consenta l'input da parte dell'utente.To implement filtering, your app should have a ListView should appear alongside a TextBox or other control that allows for user input. Il testo che l'utente digita nella casella di testo viene usato come filtro. In altre parole, vengono visualizzati solo i risultati contenenti l'input di testo o la query di ricerca.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. Durante la digitazione nella casella di testo, ListView viene costantemente aggiornato in base ai risultati filtrati. In particolare, ogni volta che il testo nella casella di testo viene modificato anche di una sola lettera, ListView scorre i propri elementi e filtra in base al termine digitato.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.

Il codice seguente mostra un'interfaccia utente con un controllo ListView semplice e il relativo DataTemplate, insieme a una casella di testo associata.The code below shows a UI with a simple ListView and its DataTemplate, along with an accompanying TextBox. In questo esempio, ListView visualizza una raccolta di oggetti Person.In this example, the ListView displays a collection of Person objects. Person è una classe definita nel code-behind (non illustrato nell'esempio di codice riportato di seguito) e ogni oggetto Person include le proprietà seguenti: 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 la casella di testo, gli utenti possono digitare un termine di ricerca o filtro per filtrare l'elenco di oggetti Person in base al cognome.Using the TextBox, users can type a search/filtering term to filter the list of Person objects by last name. Si noti che la casella di testo è associata a un nome specifico (FilterByLName) e include il proprio evento TextChanged (FilteredLV_LNameChanged).Note that the TextBox is bound to a specific name (FilterByLName) and has its own TextChanged event (FilteredLV_LNameChanged). Il nome associato consente di accedere al contenuto o al testo della casella di testo nel code-behind e l'evento TextChanged viene attivato ogni volta che l'utente digita qualcosa nella casella di testo, consentendo di eseguire un'operazione di filtro alla ricezione dell'input utente.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.

Per il corretto funzionamento del filtro, ListView deve avere un'origine dati che sia modificabile nel code-behind, ad esempio ObservableCollection<>.For filtering to work, the ListView must have a data source that can be manipulated in the code-behind, such as an ObservableCollection<>. In questo caso, la proprietà ItemsSource di ListView viene assegnata a ObservableCollection<Person> nel 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>

Filtro dei datiFiltering the data

Le query LINQ ti consentono di raggruppare, ordinare e selezionare determinati elementi di una raccolta.Linq queries allow you to group, order, and select certain items in a collection. Per filtrare un elenco, creeremo una query LINQ che selezioni solo i termini che corrispondono alla query di ricerca o al termine di filtro inserito dall'utente, immesso nella casella di testo 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. Il risultato della query può essere assegnato a un oggetto della raccolta IEnumerable.The query result can be assigned to an IEnumerable collection object. Dopo aver completato questa raccolta, possiamo usarla per un confronto con l'elenco originale, rimuovendo gli elementi non corrispondenti e aggiungendo quelli che invece corrispondono (nel caso di un movimento 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).

Nota

Per fare in modo che ListView venga animato nel modo più intuitivo durante l'aggiunta e la sottrazione di elementi, è preferibile rimuovere e aggiungere elementi alla stessa raccolta ItemsSource di ListView, anziché creare una nuova raccolta di oggetti filtrati e assegnarla alla proprietà ItemsSource di 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.

Per iniziare, dobbiamo inizializzare l'origine dati originale in una raccolta separata, ad esempio List<T> o 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>. In questo esempio abbiamo una raccolta List<Person> denominata People, che contiene tutti gli oggetti Person mostrati in ListView (la popolazione o l'inizializzazione di questo elenco non è illustrata nel frammento di codice seguente).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). Sarà anche necessario un elenco contenente i dati filtrati, che cambieranno continuamente ogni volta che viene applicato un filtro.We'll also need a list to hold the filtered data, which will constantly change every time a filter is applied. Si tratta di una raccolta ObservableCollection<Person> denominata PeopleFiltered che in fase di inizializzazione avrà lo stesso contenuto di People.This will be an ObservableCollection<Person> called PeopleFiltered, and at initialization will have the same contents as People.

L'operazione di filtro viene eseguita in base ai passaggi seguenti, illustrati nel codice riportato di seguito:The code below performs the filtering operation through the following steps, shown in the code below:

  • Impostare la proprietà ItemsSource di ListView su PeopledFiltered.Set the ListView's ItemsSource property to PeopledFiltered.
  • Definire l'evento TextChanged, FilteredLV_LNameChanged(), per la casella di testo FilterByLName.Define the TextChanged event, FilteredLV_LNameChanged(), for the FilterByLName TextBox. All'interno di questa funzione filtrare i dati.Inside this function, filter the data.
  • Per filtrare i dati, accedere alla query di ricerca o al termine di filtro inserito dall'utente tramite FilterByLName.Text.To filter the data, access the user-inputted search query/filtering term through FilterByLName.Text. Usare una query LINQ per selezionare gli elementi di People il cui cognome contiene il termine FilterByLName.Text e aggiungere gli elementi corrispondenti in una raccolta denominata 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.
  • Confrontare la raccolta PeopleFiltered corrente con gli elementi appena filtrati in TempFiltered, rimuovendo e aggiungendo elementi in PeopleFiltered se necessario.Compare the current PeopleFiltered collection with the newly filtered items in TempFiltered, removing and adding items from PeopleFiltered where necessary.
  • Quando gli elementi vengono rimossi e aggiunti in PeopleFiltered, ListView viene aggiornato e animato di conseguenza.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);
       }
   }
}

A questo punto, quando l'utente digita i termini di filtro nella casella di testo FilterByLName, ListView viene immediatamente aggiornato in modo da visualizzare solo le persone il cui cognome contiene il termine di filtro.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.

Passaggi successiviNext steps

Scaricare il codice di esempioGet the sample code