Confused about CollectionViewSource (SelectedItem not working in combos)
Asked Answered
M

2

7

I have a bunch of combos that all share the same available choices. These choices are provided in a collection exposed from my ViewModel. All fine and dandy.

I now want these choices sorted, so I decided to expose an ICollectionView from my ViewModel instead of my usual ReadonlyObservableCollection<T>, and sort the collection view in my ViewModel.

class EditStuffViewModel : ViewModelBase
{
    public EditStuffViewModel (ObservableCollection<Choice> choices)
    {
        Choices = new CollectionViewSource() { Source = choices }.View;
        Choices.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
    }

    public ICollectionView Choices
    {
        get;
        private set;
    }

    //snip other properties
}

This all works fine except that now all my combos now sync their selection.

This is not what I want. I want the choices to be shared, but selections to be to their normal bindings. I think I understand that my CollectionView is tracking selection, but I thought this was behaviour was opt in for each control.

I have tried explicitly setting IsSynchronizedWithCurrentItem="False" on my combos which successfully decouples them, but then my bound SelectedItem is never selected in the combo (the ViewModel's bound getter is called but the result is never selected). Selecting an item does seem to update my ViewModel's setter correctly.

I'm obviously missing something fundamental to how CollectionView is supposed to work. Can anyone enlighten me?

EDIT: My bad, this DOES work with IsSynchronizedWithCurrentItem="False". See my answer for details.

Cheers.

Mismanage answered 6/4, 2012 at 10:24 Comment(1)
In the solution with IsSynchronizedWithCurrentItem="False" did you use INotifyPropertyChanged? OnPropertyChanged("SelectedItem")Delocalize
M
9

I'm very sorry for wasting everyone's time, but setting IsSynchronizedWithCurrentItem="False" does work. I had also added a filter along with the sort, and the default selected values were not in the filtered list of items. Oops.

As for WHY I needed to explicitly turn IsSynchronizedWithCurrentItem off when I never normally do on standard collections, light is shed on MSDN

true if the SelectedItem is always synchronized with the current item in the ItemCollection; false if the SelectedItem is never synchronized with the current item; Nothing if the SelectedItem is synchronized with the current item only if the Selector uses a CollectionView. The default value is Nothing.

So in other words, if you use a CollectionView explicitly rather than using the default view on a normal collection, you get selection sync.

Mismanage answered 6/4, 2012 at 12:16 Comment(1)
I spent the entire day trying to figure out what was wrong, this save my evening.. Thanks a lot.. :)Partitive
C
0

I havn't touched WPF in a while but I think that you need a different instances of CollectionViewSource for each combobox for the selected item to be maintained.

I think this is because the SelectedItem property is being bound to the CollectionViewSource object's selected item state property (I'm guessing that the View object has it) and the ComboBoxes all share the same source instance, thus their selected item are now synced.

So, just use different instances of CollectionViewSource for each of your ComboBoxes. You can still share the same source choices. You just need different VMs since your ComboBoxes should behave separately from each other.


Something like this (untested):

class EditStuffViewModel : ViewModelBase
{
    public EditStuffViewModel (ObservableCollection<Choice> choices)
    {
        ChoiceViews = new List<ICollectionView>();

        for (var i = 0; i < 10; i++) {
            var viewSource = new CollectionViewSource() { Source = choices };
            viewSource.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));

            ChoiceViews.Add(viewSource.View);
        }
    }

    public IList<ICollectionView> ChoiceViews
    {
        get; private set;
    }

    //snip other properties
}

Then change your ComboBoxes binding to bind to an element of ChoiceViews instead.

Coleen answered 6/4, 2012 at 10:30 Comment(1)
Many thanks for your example code, but just isn't viable to sort a new instance for every combo. The number of combos are dynamic, and there's a lot of em. I think I'll just have to sort the collection once myself and expose it.Mismanage

© 2022 - 2024 — McMap. All rights reserved.