13

I have two ObservableCollections and I need to show them in one ListView control together. For this purpose I created MergedCollection which presents these two collections as one ObservableCollection. This way I can set the ListView.ItemsSource to my merged collection and both collections are listed. Adding works fine but when I try to Remove an item, unhandled exception is shown:

An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll
Additional information: Added item does not appear at given index '2'.

The code of MergedCollection follows:

public class MergedCollection : IEnumerable, INotifyCollectionChanged
{
    ObservableCollection<NetworkNode> nodes;
    ObservableCollection<NodeConnection> connections;

    public MergedCollection(ObservableCollection<NetworkNode> nodes, ObservableCollection<NodeConnection> connections)
    {
        this.nodes = nodes;
        this.connections = connections;

        this.nodes.CollectionChanged += new NotifyCollectionChangedEventHandler(NetworkNodes_CollectionChanged);
        this.connections.CollectionChanged += new NotifyCollectionChangedEventHandler(Connections_CollectionChanged);
    }

    void NetworkNodes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        CollectionChanged(this, e);
    }

    void Connections_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        CollectionChanged(this, e);
    }

    #region IEnumerable Members

    public IEnumerator GetEnumerator()
    {
        for (int i = 0; i < connections.Count; i++)
        {
            yield return connections[i];
        }

        for (int i = 0; i < nodes.Count; i++)
        {
            yield return nodes[i];
        }
    }

    #endregion

    #region INotifyCollectionChanged Members

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion
}

Regards

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
Zefo
  • 217
  • 1
  • 3
  • 7

2 Answers2

28

Is there any reason you can't use CompositeCollection?

The reason the exception is being thrown is because you're not translating the indexes of the inner collections to the outer. You're just passing the exact same event args to the outer event (on MergedCollection), which is why WPF doesn't find the items where the index is telling it to find them.

You use a CompositeCollection like so:

<ListBox>
  <ListBox.Resources>
    <CollectionViewSource x:Key="DogCollection" Source="{Binding Dogs}"/>
    <CollectionViewSource x:Key="CatCollection" Source="{Binding Cats}"/>
  </ListBox.Resources>
  <ListBox.ItemsSource>
    <CompositeCollection>
      <CollectionContainer Collection="{Binding Source={StaticResource DogCollection}}"/>
      <CollectionContainer Collection="{Binding Source={StaticResource CatCollection}}"/>
    </CompositeCollection>
   </ListBox.ItemsSource>
   <!-- ... -->
</ListBox>

For details, see this answer.

mrfelis
  • 736
  • 7
  • 18
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • CompositeCollection doesn't implement INotifyCollectionChanged. – Josh G Apr 22 '09 at 13:00
  • 1
    @Josh: If you follow the link, you'll see that it does. – Kent Boogaart Apr 22 '09 at 13:03
  • :-). You are right, Kent. That's perfect. I followed the link, but I didn't notice that the interfaces that the collection implements are on two lines! I only saw IList! – Josh G Apr 22 '09 at 13:15
  • Hi Kent, thank you very much, the CompositeCollection is perfect. I am still learning .NET and WPF and I somehow overlooked it. Thanks. – Zefo Apr 22 '09 at 13:24
  • Awesome, I've been looking for something like this for quite some time. Thanks, Kent! (My only complaint is that it's not strongly typed.) – Matt DeKrey Jul 02 '10 at 03:41
  • You need a crazy trick to make CompositeCollection work from XAML with Bindings. See: http://stackoverflow.com/questions/6446699/how-do-you-bind-a-collectioncontainer-to-a-collection-in-a-view-model. – Govert Jan 18 '13 at 16:42
4

You have to offset the index of the notification event.

Say you remove an item from the first collection at index 2. A collection changed event is fired with index 2.

If you remove an item from the second collection at index 2, the event is fired with the same index (2), but the item is actually enumerated after all of the items in the first collection.

Josh G
  • 14,068
  • 7
  • 62
  • 74
  • Kent's solution is better, but for the record, here's what was originally wrong with your class. – Josh G Apr 22 '09 at 13:31
  • Thank you, now I understand the problem. The exception messages are sometimes a bit tricky for me. – Zefo Apr 22 '09 at 18:00