0
\$\begingroup\$

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!

\$\endgroup\$
  • \$\begingroup\$ 1000 items to filter is like nothing and if you already experience problems with just 350 names then there is something else wrong with your code. It's not the filter function. Use a profiler to identify it or show us more. \$\endgroup\$ – t3chb0t Oct 31 '17 at 9:30
  • \$\begingroup\$ But maybe you are calling this SearchPeopleView.Refresh(); too often and pull data from the database on each search? \$\endgroup\$ – t3chb0t Oct 31 '17 at 9:31
  • \$\begingroup\$ You're only loading the People during construction? (So you have an in memory list of people that you're filtering?) Quick test I just knocked up with static data was still very responsive with 10 times that number of entities in it... \$\endgroup\$ – Pj. Oct 31 '17 at 15:52
  • \$\begingroup\$ ex: pastebin.com/WAeJCSJe \$\endgroup\$ – Pj. Oct 31 '17 at 15:58
1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.