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?
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!
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With