0

I have a class like so:

Class A
{
    public ObservableCollection<T> collectionA;
    public ObservableCollection<T> collectionB;
    public T objectA;
}

I want to expose another property:

public ObservableCollection<T> All;

that contains all of collectionA, collectionB and objectA.

Initially, I saw other questions that pointed me towards the CompositeCollection class. However, if I do that, it appears that whenever I change collectionA, the changes to it do not cause an event to propagate through the CompositeCollection and notify things that it is bound to (Some of those things, are actually my own classes).

So, my second approach was a quick and dirty function:

    public void HookCollection(ObservableCollection<T> dest, ObservableCollection<T> source)
        source.CollectionChanged += new NotifyCollectionChangedEventHandler(delegate(object sender, NotifyCollectionChangedEventArgs e)
            {
                // Apply the same changes to the destination collection
                if (e.Action == NotifyCollectionChangedAction.Reset)
                {
                    foreach (T o in source)
                    {
                        dest.Remove(o);
                    }   
                }
                if (e.NewItems != null)
                {
                    foreach (T o in e.NewItems)
                    {
                        dest.Add(o);
                    }
                }
                if (e.OldItems != null)
                {
                    foreach (T o in e.OldItems)
                    {
                        dest.Remove(o);
                    }
                }  
            });
    }

But I quickly found that in the "Reset" action, the items that were originally inside the source collection have already been destroyed, so I don't know what to remove from the destination ones.

This property is bound to a control that has some properties on it that are dependent upon the contents of this list, ie... Minimum and Maximum values of the entire set. These are in turn bound to the control. So being able to iterate over it as an IEnumerable would be very useful (and CompositeCollection makes this difficult).

As such, I need to have a clean way of being notified of changes to the list (and the sub lists).

At the moment, the only solution I can see is in my control class, detecting the CompositeCollection, then iterating over each CollectionContainer and attaching to it's CollectionChanging event if it has one. However, as you can imagine, this is a rather nasty way to hook into events. It'd be nice to have some way of aggregating those events cleanly. (I thought that was CompositeCollection but apparently not?).

Thanks for any thoughts you might have!

H.B.
  • 166,899
  • 29
  • 327
  • 400
JoshG
  • 775
  • 1
  • 7
  • 19
  • Mak, in what style would you suggest using that? Return a multiValueConverter? or when I bind to the target control, bind all the propertys from my view, and get the converter to create a merged list of all the objects?... It'd be nice to be able to expose it as one property... but it does seem less evil than the current options – JoshG Jun 30 '13 at 11:57
  • actually, a MultiValueConverter would have the same problem since it wouldn't propagate the events from the underlying containers... – JoshG Jun 30 '13 at 12:08

1 Answers1

0

according to MSDN NotifyCollectionChangedAction.Reset means that

The content of the collection changed dramatically.

the event argument in this case wont include old items, since it will probably be more efficient to re-scan the whole collection and not handle each Add Remove events.

in order to fix you current solution all you have to do is add a function which res-scans your collections and run it when you receive the NotifyCollectionChangedAction.Reset action.

private void init()
    {
        // if you want it to be thread safe dont forget to lock
        all.Clear();

        foreach (var item in this.collectionA)
        {
            all.Add(item);
        }
        foreach (var item in this.collectionB)
        {
            all.Add(item);
        }
    }

//your hooking method should look like this:

public void HookCollection(ObservableCollection<int> dest, ObservableCollection<int> source)
   {
    source.CollectionChanged += new NotifyCollectionChangedEventHandler(delegate(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Apply the same changes to the destination collection
            if (e.Action == NotifyCollectionChangedAction.Reset)
            {                   
                this.init();
            }
            if (e.NewItems != null)
            {
                foreach (int o in e.NewItems)
                {
                    dest.Add(o);
                }
            }
            if (e.OldItems != null)
            {
                foreach (int o in e.OldItems)
                {
                    dest.Remove(o);
                }
            }  
        });        

    }
makc
  • 2,569
  • 1
  • 18
  • 28
  • That wouldn't work though would it? Because if I reset one of the sub collections, it will reset the merged one entirely right? – JoshG Jun 30 '13 at 13:05
  • Nevermind, I just found this question which clears that up... http://stackoverflow.com/questions/224155/when-clearing-an-observablecollection-there-are-no-items-in-e-olditems So this may work enough to get me out of the woods. I've recently tried experimenting with a CompositeCollectionView but I can't get an IEnumerable out of it.. – JoshG Jun 30 '13 at 13:09
  • @JoshG it works i tested it, and yes when you clear your sub collection the combined collection gets updted by traversing the collections. – makc Jun 30 '13 at 13:12
  • @JoshG lol i had a copy paste bug instead off calling this.reset its supposed to be this.init() – makc Jun 30 '13 at 13:24