Xamarin ListView过滤扩展需求:基于类型开关控件实现列表过滤
Got it, let's walk through how to add those Type-based Switch filters to your Xamarin.Forms ListView. I've implemented similar functionality before, so here's a practical, step-by-step approach that integrates smoothly with your existing setup:
First, let's make sure your data and view layer are set up to handle the filter state:
a. Add a Filter Tracking Class
Create a simple helper class to track each Type's switch state (it needs to implement INotifyPropertyChanged so the UI updates when toggles change):
public class TypeFilter : INotifyPropertyChanged { private string _typeName; public string TypeName { get => _typeName; set { _typeName = value; OnPropertyChanged(); } } private bool _isEnabled = true; // Default to on public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
b. Update Your ViewModel
Add these properties to your existing ViewModel (the one bound to ItemsPage.xaml):
// All raw items from SQLite public ObservableCollection<Item> AllItems { get; set; } = new ObservableCollection<Item>(); // Filtered items displayed in the ListView public ObservableCollection<Item> FilteredItems { get; set; } = new ObservableCollection<Item>(); // Track search bar input (keep your existing binding) private string _searchText; public string SearchText { get => _searchText; set { _searchText = value; OnPropertyChanged(); ApplyFilters(); } } // Collection of Type filters with switch states public ObservableCollection<TypeFilter> TypeFilters { get; set; } = new ObservableCollection<TypeFilter>();
Then, load your unique Types from SQLite and populate the TypeFilters collection (do this in your ViewModel's constructor or initialization method):
// Fetch distinct Types from your Items table (adjust query to match your schema) var allUniqueTypes = await _sqliteConnection.QueryAsync<string>("SELECT DISTINCT TypeName FROM Items"); // Convert to TypeFilter objects foreach (var type in allUniqueTypes) { var filter = new TypeFilter { TypeName = type }; // Subscribe to toggle changes to trigger filtering filter.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(TypeFilter.IsEnabled)) ApplyFilters(); }; TypeFilters.Add(filter); }
Update ItemsPage.xaml to add a horizontal scrollable row of switches below your existing SearchBar. We'll use a CollectionView to dynamically generate switches for each Type:
<StackLayout> <!-- Your existing SearchBar --> <SearchBar Placeholder="Search items..." Text="{Binding SearchText}" Margin="10"/> <!-- Type Filter Switches (horizontal scroll) --> <ScrollView Orientation="Horizontal" Margin="10,0"> <CollectionView ItemsSource="{Binding TypeFilters}" HorizontalOptions="Start"> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout Orientation="Horizontal" Spacing="8" Margin="0,0,15,0"> <Label Text="{Binding TypeName}" VerticalOptions="Center" FontSize="Small"/> <Switch IsToggled="{Binding IsEnabled}" VerticalOptions="Center"/> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Horizontal" ItemSpacing="5"/> </CollectionView.ItemsLayout> </CollectionView> </ScrollView> <!-- Your existing ListView (bind to FilteredItems instead of AllItems) --> <ListView ItemsSource="{Binding FilteredItems}"> <!-- Keep your existing ItemTemplate here --> </ListView> </StackLayout>
Add the ApplyFilters method to your ViewModel to combine both the SearchBar text and switch states:
private void ApplyFilters() { // Start with all items var filteredItems = AllItems.AsEnumerable(); // Apply SearchBar filter first if (!string.IsNullOrWhiteSpace(SearchText)) { filteredItems = filteredItems.Where(item => item.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase)); } // Apply Type switch filter var enabledTypes = TypeFilters.Where(f => f.IsEnabled).Select(f => f.TypeName).ToList(); if (enabledTypes.Any()) // Only filter if at least one Type is enabled { filteredItems = filteredItems.Where(item => item.ItemTypes.Any(t => enabledTypes.Contains(t))); } // Update the displayed items FilteredItems.Clear(); foreach (var item in filteredItems) { FilteredItems.Add(item); } }
- All switches off: Currently, this code shows all items. If you want to show nothing instead, change the
if (enabledTypes.Any())check toif (enabledTypes.Count > 0)and add an else clause to clearFilteredItems. - Performance: If you have a large dataset, add a small delay to the
ApplyFiltersmethod (e.g.,await Task.Delay(200);) to avoid excessive filtering during rapid typing or toggling. - Initial load: Don't forget to call
ApplyFilters()once after loadingAllItemsto populate the initial filtered list.
内容的提问来源于stack exchange,提问作者MrMatt

