views:

210

answers:

2

Hello,

I'm using a ListBox to maintain a list of items in a WPF application. The ListBox data source is a HashSet wrapped in an ObservableCollection. ie, I have the following code :

this.shackSet = new ObservableCollection<Shack>(new HashSet<Shack>());
this.shackListing.ItemsSource = this.shackSet;

... where shackListing is a ListBox control, and shackSet in an ICollection. However, whenever I add anything to shackSet after the addition of the first item, I see multiple items in the ListBox. ie It's like newly added items are getting added to the list regardless of whether they're added to the set. When I look at the signatures of ICollection#Add :

void Add(T obj);

... and HashSet#Add :

bool Add(T obj);

... this leads me to believe there's a bug that affects wrapped HashSets where newly added items get added to the ListBox regardless because the ObservableCollection has no way of telling whether the object was actually added to the underlaying collection because the return type of ICollection#Add is void. Can anybody else confirm this ?

+3  A: 

When you create a new ObservableCollection with another collection you are not wrapping that collection, you create a new one where all items of the passed collection are copied to the ObservableCollection. If you want to use an ObservableCollection for the sole purpose of DataBinding, look no further, you can bind to any IEnumerable in WPF. This unfortuantely has the drawback that WPF will not always correctly pickup changes to the bound collection. If this is an issue you'd probably have to create your own obeservable hashset:

public class ObservableHashSet<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); 
    } 
}

EDIT: AS already has been pointed out, you can not inherit from HashSet to implement INotifyCollectionChanged. However if you look at the code (using Reflector) for the HashSet class it is pretty simple it should be too hard to mimic that functionality yourself.

bitbonk
Sure you can bind directly to the hashset, but if an item is added WPF won't notice.
Cameron MacFarland
+1 for the explanation. However, regarding the proposed solution, I would rather inherit from HashSet and implement INotifyCollectionChanged...
Thomas Levesque
+1 to what Thomas said, except that you can't inherit from `HashSet` and also implement `INotifyCollectionChanged`, since `HashSet` methods aren't virtual and cannot be overridden. You'll have to implement `ICollection`, and your implementation would have to _wrap_ `HashSet` and raise notifications on all mutating methods.
Pavel Minaev
@Pavel : good point... I must confess that I didn't check whether Hashset methods were virtual ;)
Thomas Levesque
A: 

As bitbonk said, ObservableCollection doesn't wrap the Hashset but copies its elements instead.

If you want an Observable Hashset check out How can I make an Observable Hashset in C# ?

Cameron MacFarland
Does Microsoft have any mechanism for submitting feature requests ? It seems to me that an Observable(Hash)Set is something that should already be in the .NET framework.
Alex Marshall
Sure, at Microsoft Connect. http://connect.microsoft.com/VisualStudio
Cameron MacFarland