19

我正在寻找and的INotifyCollectionChanged实现。我可以自己动手,但我不想重新发明轮子。StackQueue

4

4 回答 4

34

我遇到了同样的问题,想与其他人分享我的解决方案。希望这对某人有帮助。

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; }
    }
}
于 2011-06-05T19:15:41.870 回答
9

使用堆栈和队列(几乎按照定义),您只能访问堆栈顶部或队列头部。这就是它们与List. (所以,这就是你没有找到的原因)

为了回答,尽管您可以自己编写,但我会通过派生来做到这一点 ObservableCollection,然后在堆栈实现的情况下,Push作为Insert偏移量 0 (并弹出作为返回索引 0,然后返回索引RemoveAt0);或者使用队列,您可以只Add到列表的末尾Enqueue,然后抓取并删除第一个项目,就像堆栈一样,for DequeueInsert和操作将在底层调用Add,因此导致事件被触发。RemoveAtObservableCollectionCollectionChanged


您可能还说,您只是想在您应该有权访问的一项更改时绑定或收到通知。您将再次创建自己的类,派生自 Stack 或 Queue,并在以下情况下手动触发 CollectionChanged 事件:

  • 某些东西被压入堆栈或从堆栈中弹出
  • 某物从队列中出列
  • 当队列先前为空时,队列中已排队
于 2010-06-27T11:15:59.767 回答
4

我意识到已经有一些答案,但我想我会回馈一点。我将帖子和评论中提到的所有内容放在一起。有几件事促使我这样做:

  • 如其中一篇文章所述, INPC 应始终在、或被调用Count时开火。PushPopClear
  • 对于Clear,操作应该是Reset,并且集合更改事件的索引应该设置为-1(如果未设置,它将默认为无论如何其他帖子都有):.NET docs
  • 对于Push/ Pop,操作应该是Add/Remove并且集合更改事件的索引应该是0堆栈,因为它始终是并且只有第一个可以操作的项目(想想stack.GetEnumerator().MoveNext())。
  • 公开所有 3 个可用的构造函数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
}
于 2019-05-16T23:46:11.373 回答
1

与上面的类非常相似,但有一些例外:

  1. 针对 Count 的集合更改更改了发布道具
  2. 覆盖可能影响计数的 TrimExcess() b/c
  3. 将事件公开,因此我不必投射到界面
  4. 适当时将索引传递给 collectionchanged
    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;
}

    }
于 2015-02-02T15:31:40.463 回答