I have around 350 People
loading into a CollectionView
and am already noticing some lag when filtering on their Name
. This concerns me as we could easily reach> 1000 People
quickly and I need filtering to be as quick as possible.
I'm trying to avoid calling Refresh()
as I know this is expensive but I am not quite sure how to do this here. Here is my code:
C#
public PeopleViewModel()
{
using (var context = new DashboardContext())
{
People = new ObservableCollection<Person>(context.People.ToList());
SearchPeople = new ObservableCollection<SearchPerson>(
People.Select(c => new SearchPerson
{
Id = c.Id,
FullName = c.FullName.ToLower(),
DisplayName = c.FullName
}).ToList().OrderByDescending(x => x.Id));
}
SearchPeopleView = CollectionViewSource.GetDefaultView(SearchPeople) as CollectionView;
if (SearchPeopleView != null) SearchPeopleView.Filter = ViewFilter;
}
C# Filter
private void FilterPeople()
{
SearchPeopleView.Refresh();
}
private bool ViewFilter(object obj)
{
var user = obj as SearchPerson;
return user != null && user.FullName.Contains(_searchString);
}
XAML
<DataGrid Grid.Row="1" Margin="5"
SelectedItem="{Binding SelectedSearchPerson}"
ItemsSource="{Binding SearchPeopleView}"
Style="{StaticResource HomeDataGrid}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding DisplayName}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
I use a DataGrid
here as I couldn't get the styling I was looking for in a ListBox
. I also created a minimal version of a Person
, SearchPerson
so I can search on fewer properties.
Reading around there are solutions but many are < 5 years old. I'm also aware that Contains()
can be slow so I suppose I should be looking at a different approach to the Filter
itself.
I'd appreciate any help and advice, thanks!
1 Answer 1
Take a look at ReactiveExtensions (or specifically ReactiveUI). Then use something like in your VM constructor
this.WhenAnyValue(x => x.SearchString).Throttle(TimeSpan.FromMiliseconds(50).Subscribe(_ => Fitler.Refresh())
This will wait a little between each keystroke before starting the filter.
On the other hand, checkout DerivedCollection - it can move the filtering out of the UI thread, only pushing additions and removes of single items (or calling the full reset or ReactiveList if changes are big, you can set the threshold) to UI.
Still, your solution should be fast enough. Remember that it vastly depends on your DataTemplate for your filtered items - if it is complicated, most cost will be in displaying, not in filtering - especialy that it has to be done on UI thread.
SearchPeopleView.Refresh();
too often and pull data from the database on each search? \$\endgroup\$