0
votes

As I am quite new to WPF and MVVM, this might be something quite obvious and trivial, so bear with me here.

Anyway, I have a view model with these properties:

class ViewModel : INotifyPropertyChanged {
    ICollectionView Items; // a list of Items object, wraps _items field, each Item has a Date property
    string Filter; // a filter key, wraps _filter field and calls ApplyFilter() as it is changed
    void ApplyFilter(); // based on the filter key, _items.Filter gets set to some predicate
}

The properties raise the PropertyChanged event when set and all that common MVVM stuff. In the view i have a simple ItemsControl which binds to the Items property, and some fancy data template to display each Item.

A request has been made to show the items grouped by day so that you see a date header for each day and a list of Items whose Date property corresponds to the date in the header.

Since this is strictly a display issue, I decided to leave the view model as it is, but use a converter to convert the ICollectionView Items into a Dictionary where the key is the date, and the collection is a subset of Items with that date.

The ItemsControl now has a StackPanel with a TextBlock to show the date header (the dictionary key) and another ItemsControl which is basically a copy of the old one, which just listed the items (the dictionary value).

The view renders nicely, but the filter no longer works. As the control is bound to Items, and the ICollectionView implements INotifyCollectionChanged, I was expecting the filter to work as it is changing the Items list, and that the converter would be re-run to rebuild the dictionary. Well, it is not. Changing the filter does call the ApplyFilter(), and the _items.Filter gets set to the required predicate, but the view never changes. I have also tried calling the PropertyChanged for Items from the ApplyFilter, but that does not work either.

Obviously, my contrived scenario of how this should work is wrong, and to be honest I am out of ideas, apart from creating new objects which will hold the date and list of items as properties and then using a list of those in the VM. But, as I said, in my mind, this is strictly a view issue, so the model just needs to provide a list of items, and it is the view's responsibility to decide how to render them.

Any help is greatly appreciated and thanks in advance.

EDIT: Now I'm thinking that if I'm changing the _filter.Filter, then the PropertyChanged event for the Items is actually never raised, as it is in fact not changed (the internals have changed, but Items themselves are still the same ICollectionView). Hence, the converter is never again triggered.

If this is the case, how can I trigger the converter? Raising the PropertyChanged for Items after doing ApplyFilter() also did nothing.

1
I don't know how the ICollectionView interface is working, but I think the best workaround to get the changes in the view too is using ObservableCollection<T> for binding.Miklós Balogh
I think they should behave similarly as they both have INotifiCollectionChanger. Additionally, I'd rather not change the view model itself.Ivan Pintar

1 Answers

1
votes

Perhaps using ListView instead of simple ItemsControl+converter is better idea? ListView has many good functions. Such as virtualizing, grouping, etc. All you have to do, is modify your ICollectionView grouping property and apply templates(GroupStyle)

As for your problem, your current behaviour makes sense to me. If you want to converter re-run, you're supposed to create new method RefreshBinding() and do something like this;

var referenceCopy = Items;
Items = null;//make sure INotifyPropertyCHanged is fired.
Items = referenceCopy; //converter should be called again. 

you will call it after you need your converter to be re-run. But to be completely honest, just use ICollectionView+Grouping property and ListView. Or you can implement ListView functionality yourself. Using converter does not seem good solution.