ObservableCollection

De Banane Atomic
Version datée du 12 octobre 2021 à 20:26 par Nicolas (discussion | contributions) (→‎Advanced ObservableCollection)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigationAller à la recherche

Description

Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.

OnCollectionChanged

Cs.svg
var oc = new ObservableCollection<int>();
oc.CollectionChanged += OnCollectionChanged;

oc.Add(1);
oc.Add(9);
oc[0] = 2;
oc.Move(0, 1);  // move item at index 0 to index 1
oc.RemoveAt(0);
oc.Clear();

private static void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // sender: ObservableCollection

    // e.Action : Add
    // e.NewItems: ReadOnlyList<int>() { 1 }

    // e.Action : Replace
    // e.OldItems: ReadOnlyList<int>() { 1 }
    // e.NewItems: ReadOnlyList<int>() { 2 }

    // e.Action : Move
    // e.OldItems: ReadOnlyList<int>() { 2 }
    // e.NewItems: ReadOnlyList<int>() { 2 }

    // e.Action : Remove
    // e.OldItems: ReadOnlyList<int>() { 2 }

    // Clear
    // e.Action : Reset
}

Listen of item changes

Cs.svg
var oc = new ObservableCollection<Item>();
oc.CollectionChanged += OnCollectionChanged;

oc.Add(new Item { Name = "One" });
oc[0].Name = "One+";

private static void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            RegisterItemPropertyChanged(e.NewItems);
            break;
        case NotifyCollectionChangedAction.Remove:
            UnRegisterItemPropertyChanged(e.OldItems);
            break;
        case NotifyCollectionChangedAction.Replace:
            UnRegisterItemPropertyChanged(e.OldItems);
            RegisterItemPropertyChanged(e.NewItems);
            break;
        default:
            break;
    }
}

private static void RegisterItemPropertyChanged(IList newItems)
{
    foreach (INotifyPropertyChanged item in newItems)
    {
        if (item != null)
        {
            item.PropertyChanged += new PropertyChangedEventHandler(ItemPropertyChanged);
        }
    }
}

private static void UnRegisterItemPropertyChanged(IList oldItems)
{
    foreach (INotifyPropertyChanged item in oldItems)
    {
        if (item != null)
        {
            item.PropertyChanged -= new PropertyChangedEventHandler(ItemPropertyChanged);
        }
    }
}

private static void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    // sender: Item
    // e.PropertyName: Name
}

class Item : INotifyPropertyChanged
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Advanced ObservableCollection

  • Send only 1 CollectionChanged event while adding multiple elements at the same time, instead of sending 1 CollectionChanged event per item added.
  • Send only 1 CollectionChanged event while resseting the whole collection, instead of sending 1 CollectionChanged event for clear/reset and one for each item added.
ResettableObservableCollection.cs
public sealed class AdvancedObservableCollection<T> : ObservableCollection<T>
{
    private bool raiseCollectionChangedEvent = true;

    public void AddRange(IEnumerable<T> items)
    {
        try
        {
            this.raiseCollectionChangedEvent = false;
            CollectionExtensions.AddRange(this, items);

            this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
            base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
        finally
        {
            this.raiseCollectionChangedEvent = true;
        }
    }

    public void Reset(IEnumerable<T> items)
    {
        try
        {
            this.raiseCollectionChangedEvent = false;
            this.ClearItems();
            // protected override void ClearItems()
            // {
            //     this.CheckReentrancy();
            //     base.ClearItems();
            //     this.OnPropertyChanged("Count");
            //     this.OnPropertyChanged("Item[]");
            //     this.OnCollectionReset();
            // }
            // private void OnCollectionReset() => this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            CollectionExtensions.AddRange(this, items);

            base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
        finally
        {
            this.raiseCollectionChangedEvent = true;
        }
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (this.raiseCollectionChangedEvent)
        {
            base.OnCollectionChanged(e);
        }
    }
}