How to handle a CompositeCollection with CollectionView features?
Asked Answered
C

3

7

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 are welcommed.

Consanguinity answered 17/1, 2010 at 0:50 Comment(0)
D
7

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:

  • It looks like you'll get a CollectionChanged event when the filter changes, though I can't find this documented.
  • SortDescriptions is SortDescriptionCollection which is INotifyCollectionChanged, so hook up a CollectionChanged event handler on the SortDescriptions property.
  • GroupDescriptions is ObservableCollection<GroupDescription>, so hook up a CollectionChanged event handler on the GroupDescriptions property.
Deplete answered 17/1, 2010 at 1:0 Comment(8)
The problem is I am using CompositeCollection that is bound to two different CollectionViewSource objects. When I changed the location in the ListBox (which IsSynchronizedWithCurrentItem is obviously set to True) the CurrentChanging(ed) is not fired.Consanguinity
So you have two CVSes over the same collection? Dumb question, but are you sure you're subscribing to the event on the correct CVS.View? Suggest you update the question to show the code that isn't working.Deplete
Maybe you tell me how to do it. Desired is CurrentChanging should be monitored. Conclusion from all my attempts is, The event is not fired. this is not a dumb questin, I think it's more complicated than it is; I wish it's not.Consanguinity
Sorry, I meant my question ("are you sure you're subscribing to the event on the correct CVS view?") was a dumb "sanity check" question. I don't think your original question is dumb, and didn't meant to suggest that.Deplete
oh np thanks. anyway, i think im subscribing to the right CVS, I even created a test CVS that its Source is the CompositeCollection, and I don't get notified, have you ever been into this situation? any example would be appreciated. Thanks for all your effort! BTW, I checked your blog, there is amazing stuff overthere!Consanguinity
BTW, I am assuming if I add items to these collection, the CompositeCollection grows as well right? orelse the CompositeCollection only knows about 'objects' added to it, not collections.Consanguinity
If the underlying collection that you add to is INotifyCollectionChanged, then yes, the CompositeCollection will pick up the change (and you will see the new item in the ListBox). In my sample, I was using string[] so this WON'T work. (Of course, you can't add elements to arrays anyway... but you know what I mean.) If I change my code to use ObservableCollection<string>, then adding to one of the ObservableCollections causes the composite ListBox to update.Deplete
Is there a way to sort and filter the CompositeCollection? Any outabox/workaround?Consanguinity
C
0

You cant run a CollectionView on a copmposite collection, see here

Chantel answered 17/1, 2010 at 1:42 Comment(2)
The sorting and filtering is less important for me. Top priority is CurrentChanging. BTW take a look at my discussion with itowlson above, he says he's soon going to post code. let's hope for godies...Consanguinity
You can, because that's how currency works -- hadn't realised the limitations though -- thanks!Deplete
E
0

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;
        }
    }
}
Estuarine answered 12/7, 2016 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.