如果集合显示许多项或与用户交互密切相关,则筛选是实现的有用功能。 使用本文中所述的方法进行筛选可以通过大多数集合控件(包括 ListView、 GridView 和 ItemsView)来实现。 许多类型的用户输入可用于筛选集合(如复选框、单选按钮和滑块),但本文演示了根据用户的搜索,采用基于文本的用户输入并使用它实时更新 ListView。
设置用于筛选的 UI
若要实现文本筛选,应用需要 ListView 和 TextBox 或其他允许用户输入的控件。 用户键入到 TextBox 中的文本用作筛选器;也就是说,只有包含用户文本输入的结果才会显示在 ListView 中。 当用户在 TextBox 中键入时,ListView 会不断更新筛选的结果。
注释
本文演示如何使用 ListView 进行筛选。 但是,演示的筛选也可以应用于其他集合控件,例如 GridView、ItemsView 或 TreeView。
以下 XAML 显示了一个 UI,其中包含一个简单的 ListView 以及随附的 TextBox。 在此示例中,ListView 显示对象的集合 Contact
。
Contact
是代码隐藏中定义的类,每个 Contact
对象具有以下属性: FirstName
、 LastName
和 Company
。
用户可以在 TextBox 中键入筛选词,以按姓氏筛选对象列表 Contact
。 TextBox 具有其 x:Name
属性集(FilterByLastName
),因此可以在代码隐藏中访问 TextBox 的 Text 属性。 还可以处理 TextChanged 事件(OnFilterChanged
)。 每当用户在 TextBox 中输入时,便会触发 TextChanged 事件,以便你在接收用户输入时执行筛选操作。
为了使筛选功能正常工作,ListView 必须具有可以在后台代码中操作的数据源,例如 ObservableCollection<T>。 在这种情况下,ListView 的 ItemsSource 属性将分配给代码隐藏中的属性 ObservableCollection<Contact>
。
小窍门
这是 WinUI Gallery 应用 的 ListView 页面中的一个简化版本示例。 使用 WinUI Gallery 应用程序运行和查看所有代码,包括 ListView 的 DataTemplate 和 Contact
类。
<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>
筛选数据
Linq 查询允许对集合中的某些项进行分组、排序和选择。 若要筛选列表,请构造一个 Linq 查询,该查询仅选择与用户输入的筛选词匹配的项目,并在 TextBox 中 FilterByLastName
输入。 可以将查询结果分配给 IEnumerable<T> 集合对象。 一旦拥有了此集合,您可以用它与原始列表进行比较,删除那些不匹配的项,并在出现输入错误时重新添加那些匹配的项。
注释
为了使 ListView 在添加和删除项时以最直观的方式进行动画处理,请务必在 ListView 的 ItemsSource 集合本身中添加和删除项,而不是创建筛选对象的新集合,并将其分配给 ListView 的 ItemsSource 属性。
首先,需要在单独的集合(例如 List<T>)中初始化原始数据源。 在此示例中,你有一个 List<Contact>
,称为 allContacts
,它包含了所有可能显示在 ListView 中的 Contact
对象。
还需要一个集合来保存筛选的数据,每次应用筛选器时都会不断更改。 为此,你将使用 ObservableCollection<T> ,以便在集合发生更改时通知 ListView 更新。 在此示例中,它是名为 ObservableCollection<Person>
的 contactsFiltered
,是 ListView 的 ItemsSource。 在初始化时,它将具有与allContacts
相同的内容。
筛选作通过以下步骤执行,如以下代码所示:
- 将 ListView 的 ItemsSource 属性设置为
contactsFiltered
. - 处理 TextBox 的
OnFilterChanged
事件(FilterByLastName
)。 在此事件处理程序函数中,筛选数据。 - 若要筛选数据,请通过
FilterByLastName.Text
属性访问用户输入的筛选术语。 使用 Linq 查询选择allContacts
中姓氏包含FilterByLastName.Text
字符串的项,并将这些匹配项添加到名为filtered
的集合中。 - 将当前
contactsFiltered
集合与新筛选的filtered
项进行比较,在必要的情况下删除和添加项contactsFiltered
以使其匹配filtered
。 - 删除和添加
contactsFiltered
项后,ListView 会相应地更新并显示动画效果。
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);
}
}
}
}
现在,当用户在 TextBox 中的筛选字符串中 FilterByLastName
键入时,ListView 会立即更新,仅显示姓氏包含筛选字符串的人员。
获取示例代码
WinUI 3 示例集应用程序包括大多数 WinUI 3 控件、特性和功能的交互式示例。 通过 Microsoft Store 获取应用,或在 GitHub 上获取源代码
对于 UWP:WinUI 2 画廊 应用程序包含大多数 WinUI 2 控件、特性和功能的交互式示例。 从 Microsoft 应用商店 获取应用,或在 GitHub 上获取源代码。