I\'m using the MVVM design pattern, with a ListView bound to a ListCollectionView on the ViewModel. I also have several comboboxes that are used to filter the ListView. When t
Every time the user filters, your code is replacing the Filter
delegate in your collection view with a new, fresh one. Moreover, the new one only checks the particular criteria the user just selected with a ComboBox
.
What you want is a single filter handler that checks all criteria. Something like:
public MyViewModel()
{
products = new ObservableCollection<Product>();
productsView = new ListCollectionView(products);
productsView.Filter += FilterProduct;
}
public Item SelectedItem
{
//get,set omitted. set needs to invalidate filter with refresh call
}
public Type SelectedType
{
//get,set omitted. set needs to invalidate filter with refresh call
}
public Category SelectedCategory
{
//get,set omitted. set needs to invalidate filter with refresh call
}
public ICollection<Product> FilteredProducts
{
get { return productsView; }
}
private bool FilterProduct(object o)
{
var product = o as Product;
if (product == null)
{
return false;
}
if (SelectedItem != null)
{
// filter according to selected item
}
if (SelectedType != null)
{
// filter according to selected type
}
if (SelectedCategory != null)
{
// filter according to selected category
}
return true;
}
Your view can now just bind to the appropriate properties and the filtering will just work.
Every time you set Filter property you reset previous filter. This is a fact. Now how can you have multiple filters?
As you know, there are two ways to do filtering: CollectionView
and CollectionViewSource
. In the first case with CollectionView
we filter with delegate, and to do multiple filters I'd create a class to aggregate custom filters and then call them one by one for each filter item. Like in the following code:
public class GroupFilter
{
private List<Predicate<object>> _filters;
public Predicate<object> Filter {get; private set;}
public GroupFilter()
{
_filters = new List<Predicate<object>>();
Filter = InternalFilter;
}
private bool InternalFilter(object o)
{
foreach(var filter in _filters)
{
if (!filter(o))
{
return false;
}
}
return true;
}
public void AddFilter(Predicate<object> filter)
{
_filters.Add(filter);
}
public void RemoveFilter(Predicate<object> filter)
{
if (_filters.Contains(filter))
{
_filters.Remove(filter);
}
}
}
// Somewhere later:
GroupFilter gf = new GroupFilter();
gf.AddFilter(filter1);
listCollectionView.Filter = gf.Filter;
To refresh filtered view you can make a call to ListCollectionView.Refresh()
method.
And in the second case with CollectionViewSource
you use Filter
event to filter collection. You can create multiple event handlers to filter by different criteria. To read more about this approach check this wonderful article by Bea Stollnitz: How do I apply more than one filter? (archive)
Hope this helps.
Cheers, Anvaka.