I'm trying to filter an ObservableCollection that is displayed in a DataGrid. This works great the first time, but when I try to filter again, it uses the sourcecollection instead of the filtered result. Short code example:
开发者_开发问答ICollectionView view = CollectionViewSource.GetDefaultView(myCollection);
view.Filter = delegate(object item){
User user = item as User;
if(user != null && user.Name.ToLower().Contains(textbox.Text.ToLower())) return true;
return false;
};
So what I want to do is filter only the items that are shown in my DataGrid, and NOT the entire collection (of course the first time that the filter is used, it will use the entire collection).
Another option would be something like...
public void cmbYourComboBox_SelectionChanged(object sender, RoutedEventArgs e)
{
ICollectionView filteredView = CollectionViewSource.GetDefaultView(collection);
filteredView.Filter = new Predicate<object>(GetFilteredView);
dgYourDataGrid.ItemsSource = filteredView;
}
public bool GetFilteredView(object sourceObject)
{
if (HasConditionOne(sourceObject) && HasConditionTwo(sourceObject)
{
return true;
}
return false;
}
public bool HasConditionOne(object sourceObject)
{
//perform your test and evaluate the outcome
}
public bool HasConditionTwo(object sourceObject)
{
//perform your test and evaluate the outcome
}
If the source object satisfies all of the required conditions it will be deemed appropriate for display in the filtered view.
The real problem you should be fixing is the performance of CollectionView Filter, instead of implementing nested filters by feeding back the newly filtered list as source collection to next filter.
The nested filter / feedback source collection method that you "want", will cause issues when user performs several attempts of typing and removing characters because then you will not be sure which source collection applies to that filter text.
E.g. We have 100 employees and we filter it by typing "Employee Name" as "A"... This lists 50 employees with names starting with "A". Now we continue to type "H"... 10 employess from those 50 having names starting with "AH" are filtered. But now we remove "H", ideally it should use 100 employees to search new list of employees but it will use 10 employees as that is fed back to the nested filtering process.
Imagine how complicated it will get if someone frequently types and removes random characters from the filtered text!
So ground rule is You must filter in the entire source collection
Once we know this now we can try to improve the filter functionality...
Use LINQ and set the result to the ItemsSource of the DataGrid on each typed character. They work great for large collections (I had one such datagrid having 300 thousand rows and I used LINQ to perform fast filtering).
LINQ can run on a background thread and reapply result to the ItemsSource of the datagrid.
If in .Net 4.0, LINQ offers AsParallel() calls. Very effective. Uses limited number of pooled threads for filtering.
LINQ also offers AsQueryable() interface for string property name based search.
Something else (likely the DataGrid
) may be resetting the filter on your view, since you're using the default view which is shared. Use your own collection view instead:
ICollectionView view = new ListCollectionView(myList);
You could create a helper method which checks is you already set a Filter on the collections and if it is already set then you save your filtered list in a view and filter it again and set the view as the datasource of your DataGrid.
ICollectionView view = CollectionViewSource.GetDefaultView(myCollection);
And i think this part of code gives you always the whole collection and not the already filtered one.
精彩评论