18

Currently I am using an ObservableCollection within a WPF application, the application is an implementation of Conway's Game of life and works well for about 500 cells but after that it begins to slow down significantly. I originally wrote the application using a HashSet but couldn't find any way to bind the cells to a canvas.

Is there a way to get my HashSet to notify its binding object of changes? My Cell class is a simple integer X,Y pair, if the pair exists the cell is alive otherwise dead. The Cell implements INotifyPropertyChanged and overrides GetHashCode and Equals. I couldn't get the cell to display any changes, just the cells present immediately after loading. Is there any way to Bind a Hashset to items on a Canvas?

wonea
  • 4,783
  • 17
  • 86
  • 139
Michael
  • 3,498
  • 5
  • 27
  • 32

4 Answers4

15

I don't know if this will help, but here's a really simple implementation of an "observable set" that I made for a personal project. It essentially guards against inserting (or overwriting with) an item that is already in the collection.

If you wanted to you could simply return out of the methods rather than throwing an exception.

public class SetCollection<T> : ObservableCollection<T> 
{
    protected override void InsertItem(int index, T item)
    {
        if (Contains(item)) throw new ItemExistsException(item);

        base.InsertItem(index, item);
    }

    protected override void SetItem(int index, T item)
    {
        int i = IndexOf(item);
        if (i >= 0 && i != index) throw new ItemExistsException(item);

        base.SetItem(index, item);
    }
}
Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • This is very close to what I already have, I use an observablecollection and simply guarantee that all items are unique. In my code rather than through inheritance but hey i'm a wimp ;) – Michael Feb 09 '09 at 03:00
  • Yeah I'd say that this is easier than reimplementing HashSet so that it implements INotifyCollectionChanged. All the hard work is already being done by ObservableCollection for you. – Matt Hamilton Feb 09 '09 at 03:09
  • going off of reflector it appears that you would want to only expose the default constructor or implement the others yourself, as the base calls a private `CopyFrom` Method instead of `InsertItem` – Maslow Sep 08 '10 at 14:07
  • 14
    -1 because this isn't really a set, since it (in the code-behind) uses indexes. Sets gain performance because they are object-reference based. In this implementation, the same performance gain wouldn't be achieved. Actually, the performance would be far worse. – Mathias Lykkegaard Lorenzen May 29 '11 at 10:58
  • @Mathias Huh? How does this "use indexes"? It uses object references. The call to `IndexOf` will use an object reference to determine if the object is in the set. – Matt Hamilton May 29 '11 at 11:03
  • 16
    A set defines the behavior, not the implementation. This is a set, but one with O(n) for insert, update, find. – John Gibb Sep 23 '13 at 20:10
12

You have to implement INotifyCollectionChanged too, and then it should all work OK. There's another relevant SO answer which uses freezables to ensure that changes in underlying entities are also handled.

Community
  • 1
  • 1
MrTelly
  • 14,657
  • 1
  • 48
  • 81
  • Hmm, I will take a look at that, INotifyCollectionChanged might just be my missing link, thanks! – Michael Feb 09 '09 at 03:01
12

I've posted a complete ObservableHashSet here that you can use.

https://github.com/BellaCode/Public/tree/master/ObservableHashSet

It is based of reflecting on how ObservableCollection is implemented and provides the same thread-safety re-entrancy checks.

Geoff Cox
  • 6,102
  • 2
  • 27
  • 30
  • Its nice, but but to fill the `newItems`, `oldItems` collections you need to perform some really slow operations. I would probably add an option not to fill those at all and only report that something changed by raising the event. – Magnus Sep 25 '12 at 12:46
  • 8
    `int index = this.hashSet.IndexOf(item);` error `Error 1 'System.Collections.Generic.HashSet' does not contain a definition for 'IndexOf' and no extension method 'IndexOf' accepting a first argument of type 'System.Collections.Generic.HashSet' could be found (are you missing a using directive or an assembly reference?)` – AgentFire Apr 26 '13 at 06:09
  • Too many extraneous dependencies; overengineered. See below for a much simpler solution. – Dmitri Nesteruk Sep 28 '16 at 10:22
0

Or you can steal take it from EntityFramework Core

https://github.com/dotnet/efcore/blob/main/src/EFCore/ChangeTracking/ObservableHashSet.cs

Kebechet
  • 1,461
  • 15
  • 31