Is there a way to get notified when CompositeCollection\'s current location changes?
I need to have the CompositeCollection monitored by a CollectionView, any ideas
You cant run a CollectionView on a copmposite collection, see here
I ran into the same problem: I needed sorting of a CompositeCollection. I wrote the following class that solves the problem, at least for ObservableCollections of the same type.
The idea is to maintain the composite collection as an ordinary observable collection, and update it as the underlying collections change. Then the resulting collection (AllNodes) can be used in the user interface, and it supports CollectionView just fine.
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace Util {
public class ObservableCollectionCollector<T> {
private class ReplacableObservableCollection : ObservableCollection<T> {
public void Replace(int idx, T v) {
SetItem(idx, v);
}
}
private readonly ReplacableObservableCollection allNodes;
private readonly ObservableCollection<T>[] colls;
private readonly int[] lens;
public ObservableCollectionCollector(params ObservableCollection<T>[] colls) {
this.colls = colls;
allNodes = new ReplacableObservableCollection();
foreach (var l in colls) {
foreach (var e in l)
allNodes.Add(e);
l.CollectionChanged += HandleCollectionChanged;
}
lens = colls.Select(c => c.Count).ToArray();
}
public ReadOnlyObservableCollection<T> AllNodes {
get { return new ReadOnlyObservableCollection<T>(allNodes); }
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
int i0 = 0;
int ci = 0;
foreach (var l in colls) {
if (l == sender)
break;
i0 += l.Count;
++ci;
}
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
for (int i = 0; i < e.NewItems.Count; ++i)
allNodes.Insert(i0 + e.NewStartingIndex + i, (T)e.NewItems[i]);
break;
case NotifyCollectionChangedAction.Move:
for (int i = 0; i < e.OldItems.Count; ++i)
allNodes.Move(i0 + e.OldStartingIndex + i, i0 + e.NewStartingIndex + i);
break;
case NotifyCollectionChangedAction.Remove:
for (int i = 0; i < e.OldItems.Count; ++i)
allNodes.RemoveAt(i0 + e.OldStartingIndex);
break;
case NotifyCollectionChangedAction.Replace:
for (int i = 0; i < e.NewItems.Count; ++i)
allNodes.Replace(i0 + e.OldStartingIndex + i, (T)e.NewItems[i]);
break;
case NotifyCollectionChangedAction.Reset:
for (int i = 0; i < lens[ci]; ++i)
allNodes.RemoveAt(i0);
break;
}
lens[ci] = ((ObservableCollection<T>)sender).Count;
}
}
}
You can detect when the current item has changed by monitoring the ICollectionView.CurrentChanged
event of your CollectionView. The following code works for me:
CompositeCollection cc = new CompositeCollection();
cc.Add(new CollectionContainer { Collection = new string[] { "Oh No!", "Fie" } });
cc.Add(new CollectionContainer { Collection = new string[] { "Zounds", "Ods Bodikins" } });
CollectionViewSource cvs = new CollectionViewSource { Source = cc };
// Subscribing to CurrentChanged on the ICollectionView
cvs.View.CurrentChanged += (o, e) => MessageBox.Show("current changed");
lb.ItemsSource = cvs.View; // lb is a ListBox with IsSynchronizedWithCurrentItem="True"
When I change the selection in the ListBox, the message box displays.
Regarding filtering, sorting and grouping, as per Aron's answer these are not available on a view over a CompositeCollection. But for the record here are the ways you can detect changes for views that do support these features:
ObservableCollection<GroupDescription>
, so hook up a CollectionChanged event handler on the GroupDescriptions property.