Search code examples
c#wpfcollectionview

ICollectionView - Filter and show all items in group


How can I filter items but show all items in a group when one of the items in the group matches the filter (in a performant way)?

This is my code and I search for "John" and would like to see John and Sammy (same group = male) but not Jane. Currently it only shows John.

public partial class FilteringSample : Window
    {
        public FilteringSample()
        {
            InitializeComponent();
            List<User> items = new List<User>();
            items.Add(new User() { Name = "John Doe", Age = 42, Sex = SexType.Male });
            items.Add(new User() { Name = "Jane Doe", Age = 39, Sex = SexType.Female });
            items.Add(new User() { Name = "Sammy Doe", Age = 13, Sex = SexType.Male });
            lvUsers.ItemsSource = items;

            CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
            PropertyGroupDescription groupDescription = new PropertyGroupDescription("Sex");
            view.GroupDescriptions.Add(groupDescription);           
            view.Filter = UserFilter;
        }

        private bool UserFilter(object item)
        {
            if(String.IsNullOrEmpty(txtFilter.Text))
                return true;
            else
                return ((item as User).Name.IndexOf(txtFilter.Text, StringComparison.OrdinalIgnoreCase) >= 0);
        }

        private void txtFilter_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            CollectionViewSource.GetDefaultView(lvUsers.ItemsSource).Refresh();
        }
    }

    public enum SexType { Male, Female };

    public class User
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public string Mail { get; set; }

        public SexType Sex { get; set; }
    }

Solution

  • How about this? "Performant" will depend on how big of a collection you are dealing with and what kind of performance you need.

    private List<SexType> _currentFilters = new List<SexType>();
    
    public FilteringSample()
    {
        InitializeComponent();
    
        List<User> items = new List<User>();
        items.Add(new User() { Name = "John Doe", Age = 42, Sex = SexType.Male });
        items.Add(new User() { Name = "Jane Doe", Age = 39, Sex = SexType.Female });
        items.Add(new User() { Name = "Sammy Doe", Age = 13, Sex = SexType.Male });
        lvUsers.ItemsSource = items;
    
        UpdateFilters();
    
        CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
        PropertyGroupDescription groupDescription = new PropertyGroupDescription("Sex");
        view.GroupDescriptions.Add(groupDescription);
        view.Filter = UserFilter;
    }
    
    private bool UserFilter(object item)
    {
        return _currentFilters.Contains(((User)item).Sex);
    }
    
    private void txtFilter_TextChanged(object sender, TextChangedEventArgs e)
    {
        UpdateFilters();
        CollectionViewSource.GetDefaultView(lvUsers.ItemsSource).Refresh();
    }
    
    private void UpdateFilters()
    {
        //Select the SexType(s) of the Users matching the current txtFilter name filter, or of all Users if txtFilter is empty
        string searchString = txtFilter.Text;
        IEnumerable<User> users = lvUsers.ItemsSource as IEnumerable<User>;
    
        if(!String.IsNullOrWhiteSpace(searchString))
            users = users.Where(u => u.Name.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0);
    
        _currentFilters = users.Select(u => u.Sex)
                                .Distinct()
                                .ToList();
    }