Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing own ObservableCollection

For my WPF project I need an observable collection that always keeps the correct order. My idea is to use a SortedSet<T> and implement my own AddAndNotify and RemoveAndNotify methods. In them, I would call NotifyPropertyChanged, like this:

public class ObservableSortedSet<T> : SortedSet<T>, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    public void AddAndNotify(T element)
    {
        Add(element);
        NotifyPropertyChanged(...);
    }

    public void RemoveAndNotify(T element)
    {
        Remove(element);
        NotifyPropertyChanged(...);
    }
}

But which property (or properties) would that be?

How can one implement a collection that tells the UI to update whenever the collection contents change?

Or is there an easier way by using the SortedSet directly in my ViewModel?

EDIT:

I don't want to use the predefined ObservableCollection together with a sorted view. I know this is possible by using either CollectionViewSource or converters, but these solutions don't appeal to me. I have hierarchichal data for which CollectionViewSource doesn't work, and I consider the converter version a horrible workaround for the limits of CollectionViewSource. I want to use a clean solution.

So this question is not a duplicate of how to sort ObservableCollection . I don't want to sort an ObservableCollection, I want to use a SortedSet that can tell changes to the UI.

like image 218
Kjara Avatar asked Sep 25 '26 19:09

Kjara


2 Answers

You should implement INotifyCollectionChanged Interface instead.

public class ObservableSortedSet<T> : SortedSet<T>, INotifyPropertyChanged, INotifyCollectionChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public void NotifyPropertyChanged(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    public void AddAndNotify(T element)
    {
        Add(element);
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
    }

    public void RemoveAndNotify(T element)
    {
        Remove(element);
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove));
    }
}
like image 77
ivayle Avatar answered Sep 27 '26 07:09

ivayle


I managed to do it this way:

public class ObservableSortedSet<T> : SortedSet<T>, INotifyCollectionChanged where T : IComparable<T>
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public new void Add(T element)
    {
        base.Add(element);
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public override void Clear()
    {
        base.Clear();
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public new void Remove(T element)
    {
        base.Remove(element);
        CollectionChanged?.Invoke(this,
            new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

Why did I always give the Reset action as EventArg?

  • Reason for Remove: It seems to be unable to be utilized if the collection does not have indices (see Implementing INotifyCollectionChanged on a collection without indexes or Observable Stack and Queue - in both cases it boils down to not being able to specify the correct index, resulting in a runtime error. Look at all the comments below the answers saying "index xyz doesn't work for me!".)

  • Reason for Add: Internally, the Collection IS sorted, but the UI does not reflect this correctly. My guess is that by giving the EventArg new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, element); the GUI only gets notified that the new element should be added TO THE END.

According to https://msdn.microsoft.com/en-us/library/system.collections.specialized.notifycollectionchangedaction(v=vs.100).aspx, Reset stands for "The content of the collection changed dramatically." This is kind of true for SortedSet, since its internal structure can change a lot if just one element is added or removed. So maybe my solution isn't even a workaround (even if it feels like one)!

like image 26
Kjara Avatar answered Sep 27 '26 07:09

Kjara