2

I am trying to copy an ObservableCollection into an ObservableCollection-derived type, and I want to preserve the CollectionChanged event.

So far I have:

public class PartitionCollection : ObservableCollection<AislePartition>
{
    public PartitionCollection(ObservableCollection<AislePartition> other)
    : base(other)
    {

    }

    // ...

    protected override void InsertItem(int index, AislePartition item)
    {
         // Important custom logic runs here
         if (/* */)
             base.InsertItem(index, item);
         else
             Merge(item);
    }

    protected void Merge(AislePartition item)
    {
         // ...
    }
}

It copies the collection fine but I do need to get the CollectionChanged event too.

Any way of doing that ? Thanks

EDIT:

Before: Before the constructor call After: After the constructor call

The code that uses this particular constructor:

    private static void OnSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        AisleSelection aisleSelect = args.NewValue as AisleSelection;
        if (aisleSelect.Partitions == null)
            aisleSelect.Partitions = new PartitionCollection();
        else
            aisleSelect.Partitions = new PartitionCollection(aisleSelect.Partitions);
    ...
    }

Essentially what I'm trying to do is replace the ObservableCollection with my PartitionCollection which overrides a few key members. The ObservableCollection is passed down to me from the server in serialized form, so there is no way I can use it directly.

Julien Lebot
  • 3,092
  • 20
  • 32
  • What do you mean by "cloning an event"? – ken2k Aug 10 '12 at 15:57
  • I mean I need to copy all the handlers into a new event, and discard the old one. In C++ this.CollectionChangedEvent = std::move(other.CollectionChangedEvent). But this isn't C++ :( – Julien Lebot Aug 10 '12 at 16:24

2 Answers2

3

If I understand your question correctly you want to replace on observable collection with another but you still want subscribers to events on the old collection to be notified when the new collection changes.

If that is correct what you want is to keep the old collection synchronized with the new collection. The new collection should keep an reference to the old (source) collection and whenever the new collection is modified you perform the same modification on the old collection.

In PartitionCollection you can override ClearItems, InsertItem, RemoveItem and SetItem and forward the changes to the old collection after calling the base class implementation of the methods in the new collection. This should keep the synchronized.

If you absolutely only want a sinlge copy of the collection you can derive PartitionCollection from IObservableCollection<AislePartition> and provide you own implementation of the interface. The backing store for this collection should then be the old collection. This solution is quite tedious because you have to implement a lot of methods that simply are forwarders to the same methods on the wrapped collection but you would ensure that the collection wasn't copied.

If the old collection could be modified by other means than through your wrapper you will have to subscribe to changes on the old collection and fire similar events from the wrapper.

There are some "evil" tricks that may allow you to get access to the invocation list of the old collection if you really want to. However, I would think twice before doing that.

Community
  • 1
  • 1
Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
  • No, he's providing another object and he wants to copy the events handlers from the other object to the new one in the constructor. – Jorge Córdoba Aug 10 '12 at 16:05
  • @JorgeCórdoba: But is that sensible? If I add an event handler to an event on A and then later the event is fired but from B, isn't that unexpected? I didn't add the handler to B - only to A. – Martin Liversage Aug 10 '12 at 16:10
  • Yeah, you're right but that's the context of the question. What is "sensible" or not really depends on a lot of factors, the better part of them related to milestones, delivery pressure and management pressure :) I'm not saying it's a good practice, I'm saying is the way I'll solve it but as you say it's kind of weird to do that. – Jorge Córdoba Aug 10 '12 at 16:15
  • Yes I should have explained my intent better, the original object is to be discarded, I just want to keep its event handler and collection item, because I replace it with a more specialized class (PartitionCollection). The ObservableCollection is intantiated somewhere else where I don't have the control to change it. – Julien Lebot Aug 10 '12 at 16:18
  • I think you understand correctly, the only thing is that I don't need the old collection and I thought it could be wasteful to have two copies around. Or it could be I'm misunderstanding the solution. Coming from a C++ background, I half expected something like this.CollectionChanged = other.CollectionChanged to work. – Julien Lebot Aug 10 '12 at 16:27
  • Instead of hackering around I ended up scraping the class and using aggregation and keep a "pimpl" (intenal collection copy) and I forward everything to it. I have to say this has been one of the rare times where C++ has been more elegant than C# :P – Julien Lebot Aug 10 '12 at 17:05
1

You can't just copy the event handlers from one object to another object because events provide just two methods to add a handler and remove a handler... in other words you can't enumerate the handlers and add them to your new object event.

What I'd suggest would be to make a weak (or strong) to the old object and provide an internal event handler that invokes first all the subscribers of your event and then invokes the event on the provided object.

Keep in mind that if you use a strong reference the old object will be kept alive as long as the current object is alive, so that could potentially lead to memory leaks or excessive memory usage.

Jorge Córdoba
  • 51,063
  • 11
  • 80
  • 130
  • Sounds interesting, but the original object is to be destroyed (I am replacing it with the new one). Worst case scenario I do just that since I can guarantee there will be only one instance of that particular object in the entirety of the program. – Julien Lebot Aug 10 '12 at 16:16