Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Filtering in MVVM architecture

DZone's Guide to

Filtering in MVVM architecture

· Database Zone
Free Resource

Check out the IT Market Clock report for recommendations on how to consolidate and replace legacy databases. Brought to you in partnership with MariaDB.

Wpf has great filtering capabilities based on CollectionViewSource class, but how can we use effectively in MVVM architecture? First of all we need to decide who has the responsibility of filtering data, and clearly the answer is: the ViewModel. VM  usually exposes an ObservableCollection<T> property for collections of items, the view bind to it and everything goes smooth, until you need to set a filter on it. A possible solution is to grab a reference to an ICollectionView in the viewmodel and manipulate that object.

   1: ListCollectionView lcv = CollectionViewSource.GetDefaultView(myCollection) as ListCollectionView;

2: lcv.SortDescriptions.Add(new SortDescription(…));


I do not like very much this approach, are you sure that a valid default view exists during a UnitTest? So lets examine another possible solution.

Suppose you have a view that has a ListView currently bound to a property of ViewModel called Links, and this property is an ObservableCollection<T>, suppose also that you need to filter content based on a property called Status. What I want is “no code” on the View, and minimum impact on the already existing view, I want also the ability to test the filter function with a unit test.

For the first requirements, it is enough to expose to the view “something” bindable in the same property name (Links), since my ViewModel has an ObservableCollection<SingleAnalysisLink> called Links, I changed the viewmodel in this way.

   1: internal ObservableCollection<SingleAnalysisLink> InnerLinks { get; set; };

2:

3: internal CollectionViewSource CvsLinks{get; set; }

4:

5: public ICollectionView Links

6: {

7: get { return CvsLinks.View; }

8: }


The real ObservableCollection is now internal, then I added a CollectionViewSource property and finally the Links readonly property that return the View of the CollectionViewSource property (that is perfectly bindable). Now during VM initialization I need to setup everything.

   1: InnerLinks = new ObservableCollection<SingleAnalysisLink>();

2: CvsLinks = new CollectionViewSource();

3: CvsLinks.Source = InnerLinks;

4: CvsLinks.Filter += ApplyFilter;


I create the ObservableCollection, the new CollectionViewSource and add the collection to the source, then bound the event ApplyFilter wrote this way.

   1: void ApplyFilter(object sender, FilterEventArgs e)

2: {

3: //each item is a specific object

4: SingleAnalysisLink si = (SingleAnalysisLink) e.Item;

5: if (StatusFilter == null)

6: {

7: e.Accepted = true;

8: } else

9: {

10: e.Accepted = si.Status == StatusFilter;

11: }

12: }

13: public SingleAnalysisStatus? StatusFilter

14: {

15: get { return _statusFilter; }

16: set {

17: this.Set(p => p.StatusFilter, value, ref _statusFilter);

18: OnFilterChanged();

19: }

20: }

21:

22: private SingleAnalysisStatus? _statusFilter;

23:

24: private void OnFilterChanged()

25: {

26: CvsLinks.View.Refresh();

27:

28: }


The function ApplyFilter filters object based on another property of the VM called StatusFilter, of the same type of the enum property used in the bound object. In the setter part of the StatusFilter property simply call a function that refresh the view. Now I can simply add a combo in the View, bind the SelecteValue property to the StatusFilter VM property and without any other change I’ve added filtering capabilities to the View.

The cool part is that I’m able to write unit test that verify filtering capabilities.

   1: ...

2: sut.AddUrlToAnalyze(link);

3: sut.AddUrlToAnalyze(link2);

4:

5: link2.Status = SingleAnalysisStatus.Match;

6: sut.StatusFilter = SingleAnalysisStatus.Match;

7: sut.Links.OfType<SingleAnalysisLink>()

8: .Count().Should().Be.EqualTo(1);


I’ve added two SingleAnalysisLink to the VM, then change the status of only one of them to Match, set a filter to show only item with status == Match and assert that the count of items of the view is Equal To 1.

Interested in reducing database costs by moving from Oracle Enterprise to open source subscription?  Read the total cost of ownership (TCO) analysis. Brought to you in partnership with MariaDB.

Topics:

Published at DZone with permission of Ricci Gian Maria, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}