CBreeze
CBreeze

Reputation: 2965

Filter Method of ICollectionView and Properly Binding and ObservableCollection in MVVM

I'm getting stuck into MVVM in WPF and I have setup an ObservableCollection and an ICollectionView. The ICollectionView is set as the ItemsSource of a DataGrid, and the model is a type of Job.

I've setup getters and setter for both of the collections however when I am setting a Filter on the ICollectionView instead of the Job being filtered by the SearchString they're just replicated over and over again, leading me to believe that they way I have the collections setup is totally wrong.

Here is how the two collections are get/set:

public ObservableCollection<Job> AllJobs
{
    get
    {
        foreach (var job in _allJobsList)
            _allJobs.Add(job);
        return _allJobs;
    }
    set
    {
        if (_allJobs == value) return;
        OnPropertyChanged("AllJobs");
    }
}

public ICollectionView AllJobsView
{
    get
    {
        _allJobsView = CollectionViewSource.GetDefaultView(AllJobs);
        return _allJobsView;
    }
    set
    {
        if (_allJobsView == value)
        {
            return;
        }

        _allJobsView = value;
        OnPropertyChanged("AllJobsView");
    }
}

Now I have a stringcalled SearchString that is bound to a TextBox.Text. When the text changes I do the following:

public string SearchString
{
    get => _searchString;
    set
    {
        if (_searchString == value) return;
        _searchString = value;
        FilterJobs();
        OnPropertyChanged("SearchString");
    }
}

private void FilterJobs()
{
    AllJobsView.Filter = x =>
    {
        var viewJob = x as Job;
        return viewJob != null && viewJob.Number.Contains(_searchString);
    };
}

Now when the page first loads, there are the correct Jobs loaded into the DataGrid. However, as soon as the user types the Jobs are duplicated if the Job.Number does contain the SearchString. How am I able to setup the collections so that I can appropriately set a filter?

Upvotes: 0

Views: 301

Answers (1)

Daniel Marques
Daniel Marques

Reputation: 703

The problem is in the getter of your ObservableCollection. Every time you "get" the collection, you are adding every item to the collection all over again.

Your code:

get
{
    foreach (var job in _allJobsList)
        _allJobs.Add(job);
    return _allJobs;
}

Instead, it should be:

get
{        
    return _allJobs;
}

The setter of your ObservableCollection is also missing the "setter" (private field = value) code:

set
{
    if (value != _allJobs)
    {
        _allJobs = value;
        OnPropertyChanged("AllJobs");
    }
}

Your Property AllJobs would then be:

private ObservableCollection<Job> _allJobs;
public ObservableCollection<Job> AllJobs
{
    get
    {        
        return _allJobs;
    }
    set
    {
        if (value != _allJobs)
        {
            _allJobs = value;
            OnPropertyChanged("AllJobs");
        }
    }
}

The initialization of your collection should be someplace else (and not in the getter of your property), like in the constructor of the ViewModel or/and in a method that a command calls after the user asks for a refresh of the collection.

For example, if your VieModel is called MyViewModel and your List<Job> is called _allJobsList, you can initialize your collection like so:

public MyViewModel()
{
    //fill the _allJobsList first, getting from a database for example: _allJobsList = GetJobs();
    //and then create an observable collection from that list
    AllJobs = new ObservableCollection<Job>(_allJobsList);
}

Upvotes: 1

Related Questions