Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wpf ListBoxes' ItemsSource strange behaviour

Tags:

.net

wpf

.net-4.0

Edited to address F Ruffell's answer

I have the following xaml

<StackPanel>
    <ListBox x:Name="_list1"/>
    <ListBox x:Name="_list2"/>
</StackPanel>

and this code-behind:

var ints = new[] { 1, 2, 3 };
_list1.ItemsSource = ints;
_list2.ItemsSource = ints;

_list1.Items.Filter = i => ((int)i) < 2;

For some reason after setting filter only for the first ListBox both lists are filtered. I expect the lists to have completely different CollectionViews and indeed _list1.Items != _list2.Items. Meanwhile setting filter to one of them also somehow sets that very filter to the other.
The question is why and how are CollectionViews synchronized?

like image 511
alpha-mouse Avatar asked Oct 10 '11 13:10

alpha-mouse


2 Answers

When you set the ItemsSource WPF actually creates a CollectionView from the specified IEnumerable. It does this so that there is a notion of a selected item(s), filtering, grouping etc (none of which are supported by the IEnumerable assigned to the ItemsSource). When the same underlying collection is used more than once, WPF will synchronized the two CollectionViews. If you don't want this behaviour, simply set IsSynchronizedWithCurrentItem to False on each ListBox.

For more information see:

WPF Combobox binding

Edit

Upon further investigation it appears as though setting IsSynchronizedWithCurrentItem really only applies to the selected item, and all other properties across the two ICollectionViews are still synchronized (even though each ListBox has its own ICollectionView - changing the Filter or adding a SortDescription to one, will add it to the other, you learn something new everyday :) ).

To change this behaviour, you need to create the ICollectionView for each ListBox yourself and then directly modify the Filter property, for example:

        var ints = new[] { 1, 2, 3 };
        var viewOne = new ListCollectionView(ints);
        var viewTwo = new ListCollectionView(ints);
        _list1.ItemsSource = viewOne;
        _list2.ItemsSource = viewTwo;

        viewOne.Filter = i => ((int)i) < 2;

Cheers!

like image 188
FunnyItWorkedLastTime Avatar answered Nov 08 '22 03:11

FunnyItWorkedLastTime


The question is why and how are CollectionViews synchronized?

They are synchronized because even though both ListBoxes have different Items, they share the same CollectionView, which is the default view for the source collection.

The Items property of ItemsControl is of type ItemCollection and the CollectionView property of ItemCollection is internal so we can't access it directly to verify that this is true. However, we can just enter these three values in the debugger to verify this, they all come out as true

_list1.Items.CollectionView == _list2.Items.CollectionView // true
_list1.Items.CollectionView == CollectionViewSource.GetDefaultView(ints) // true
_list2.Items.CollectionView == CollectionViewSource.GetDefaultView(ints) // true

Alternatively, we can use reflection to do the comparison in code

PropertyInfo collectionViewProperty =
    typeof(ItemCollection).GetProperty("CollectionView", BindingFlags.NonPublic | BindingFlags.Instance);
ListCollectionView list1CollectionView = collectionViewProperty.GetValue(_list1.Items, null) as ListCollectionView;
ListCollectionView list2CollectionView = collectionViewProperty.GetValue(_list2.Items, null) as ListCollectionView;
ListCollectionView defaultCollectionView = CollectionViewSource.GetDefaultView(ints) as ListCollectionView;

Debug.WriteLine(list1CollectionView == list2CollectionView);
Debug.WriteLine(list1CollectionView == defaultCollectionView);
Debug.WriteLine(list2CollectionView == defaultCollectionView);

The way to work around this has already been posted by F Ruffell, create a new ListCollectionView as ItemsSource for each ListBox.

_list1.ItemsSource = new ListCollectionView(ints);
_list2.ItemsSource = new ListCollectionView(ints);

Also note that after this, the 3 comparisons above comes out as false

like image 26
Fredrik Hedblad Avatar answered Nov 08 '22 05:11

Fredrik Hedblad