Наблюдаемый стек и очередь - PullRequest
15 голосов
/ 27 июня 2010

Я ищу INotifyCollectionChanged реализацию Stack и Queue. Я мог бы катиться самостоятельно, но я не хочу изобретать велосипед.

Ответы [ 4 ]

31 голосов
/ 05 июня 2011

Я столкнулся с той же проблемой и хочу поделиться своим решением с другими.Надеюсь, это кому-нибудь пригодится.

public class ObservableStack<T> : Stack<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
    public ObservableStack()
    {
    }

    public ObservableStack(IEnumerable<T> collection)
    {
        foreach (var item in collection)
            base.Push(item);
    }

    public ObservableStack(List<T> list)
    {
        foreach (var item in list)
            base.Push(item);
    }


    public new virtual void Clear()
    {
        base.Clear();
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public new virtual T Pop()
    {
        var item = base.Pop();
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
        return item;
    }

    public new virtual void Push(T item)
    {
        base.Push(item);
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
    }


    public virtual event NotifyCollectionChangedEventHandler CollectionChanged;


    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        this.RaiseCollectionChanged(e);
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        this.RaisePropertyChanged(e);
    }


    protected virtual event PropertyChangedEventHandler PropertyChanged;


    private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (this.CollectionChanged != null)
            this.CollectionChanged(this, e);
    }

    private void RaisePropertyChanged(PropertyChangedEventArgs e)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, e);
    }


    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
    {
        add { this.PropertyChanged += value; }
        remove { this.PropertyChanged -= value; }
    }
}
9 голосов
/ 27 июня 2010

С помощью стеков и очередей (почти по определению) у вас есть доступ только к вершине стека или началу очереди. Это то, что отличает их от List. (и поэтому вы его не нашли)

Чтобы ответить, хотя вы могли бы написать свой собственный, я бы сделал это, выведя из ObservableCollection, затем в случае стека, реализующего Push как Insert со смещением 0 (и выдаваемый как возвращающий индекс 0 затем RemoveAt индекс 0); или с очередью вы можете просто Add до конца списка набрать Enqueue, а затем взять и удалить первый элемент, как со стеком, для Dequeue. Операции Insert, Add и RemoveAt будут вызваны для базового ObservableCollection, что вызовет событие CollectionChanged.


Возможно, вы также говорите, что просто хотите связать или получить уведомление, когда один элемент, к которому вы должны иметь доступ, изменится. Вы снова создадите свой собственный класс, полученный из стека или очереди, и запустите событие CollectionChanged вручную, когда:

  • Что-то помещено в стек или извлечено из него
  • Из очереди что-то исключено
  • Что-то в очереди в очереди, когда очередь ранее была пустой
1 голос
/ 02 февраля 2015

Очень похоже на приведенный выше класс, с некоторыми исключениями:

  1. Изменена публикация реквизита для изменений коллекции для Count
  2. Переопределить TrimExcess () b / c, который может повлиять на Count
  3. Обнародовал события, поэтому мне не нужно приводить к интерфейсу
  4. Передает индекс в коллекцию, измененную при необходимости
    public class ObservableStack : Stack, INotifyPropertyChanged, INotifyCollectionChanged
    {
      public ObservableStack(IEnumerable collection) : base(collection) {}
      public ObservableStack() { } 

      public event PropertyChangedEventHandler PropertyChanged = delegate { };
      public event NotifyCollectionChangedEventHandler CollectionChanged = delegate { };

      protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, List items, int? index = null)
      {
        if (index.HasValue)
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items, index.Value));
        }
        else
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items));
        }
         OnPropertyChanged(GetPropertyName(() => Count));
      }

      protected virtual void OnPropertyChanged(string propName)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }

      public new virtual void Clear()
      {
        base.Clear();
        OnCollectionChanged(NotifyCollectionChangedAction.Reset, null);
      }

      public new virtual T Pop()
      {
        var result = base.Pop();
        OnCollectionChanged(NotifyCollectionChangedAction.Remove, new List() { result }, base.Count);
        return result;
      }

      public new virtual void Push(T item)
      {
        base.Push(item);
        OnCollectionChanged(NotifyCollectionChangedAction.Add, new List() { item }, base.Count - 1);
      }   

      public new virtual void TrimExcess()
      {
        base.TrimExcess();
        OnPropertyChanged(GetPropertyName(() => Count));
      }

String GetPropertyName(Expression> propertyId)
{
   return ((MemberExpression)propertyId.Body).Member.Name;
}

    }
0 голосов
/ 17 мая 2019

Я понимаю, что уже есть несколько ответов, но подумала, что немного отдам свои.Я собрал все упомянутое в постах и ​​комментариях.Было несколько вещей, которые побудили меня сделать это:

  • INPC всегда должен запускать для Count, когда вызываются Push, Pop или Clear, как упоминалось в одном изposts.
  • Для Clear, действие должно быть Reset, а индекс для события изменения коллекции должен быть установлен на -1 (который по умолчанию будет установлен в любом случае, если не установлен, так что другие сообщения имеют это): .NET docs
  • Для Push / Pop действие должно быть Add / Remove, а индекс для измененного события коллекции должен составлять 0 для стекав том, что это всегда и только первый элемент, который может быть манипулирован (например, stack.GetEnumerator().MoveNext()).
  • Предоставляет доступ ко всем трем конструкторам, доступным в Stack<T>, и использует вызовы base(), поскольку нет причин переопределятьлогика.

Результат:

public class ObservableStack<T> : Stack<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
    #region Constructors

    public ObservableStack() : base() { }

    public ObservableStack(IEnumerable<T> collection) : base(collection) { }

    public ObservableStack(int capacity) : base(capacity) { }

    #endregion

    #region Overrides

    public virtual new T Pop()
    {
        var item = base.Pop();
        OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);

        return item;
    }

    public virtual new void Push(T item)
    {
        base.Push(item);
        OnCollectionChanged(NotifyCollectionChangedAction.Add, item);
    }

    public virtual new void Clear()
    {
        base.Clear();
        OnCollectionChanged(NotifyCollectionChangedAction.Reset, default);
    }

    #endregion

    #region CollectionChanged

    public virtual event NotifyCollectionChangedEventHandler CollectionChanged;

    protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, T item)
    {
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(
            action
            , item
            , item == null ? -1 : 0)
        );

        OnPropertyChanged(nameof(Count));
    }

    #endregion

    #region PropertyChanged

    public virtual event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string proertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(proertyName));
    }

    #endregion
}
...