Я ищу реализацию INotifyCollectionChanged
Stack
и Queue
. Я мог бы свернуть самостоятельно, но я не хочу изобретать велосипед.
Наблюдаемый стек и очередь
Ответ 1
С помощью стеков и очередей (почти по определению) у вас есть доступ только к вершине стека или главы очереди. Это отличает их от List
. (и поэтому, почему вы его не нашли)
Чтобы ответить, хотя вы можете написать свой собственный, я бы сделал это, получив ObservableCollection
, а затем в случае стека, реализующего Push
как Insert
при смещении 0 (и pop как возвращающий индекс 0 то RemoveAt
индекс 0); или с помощью очереди вы можете просто Add
до конца списка до Enqueue
, а захватить и удалить первый элемент, как и стек, для Dequeue
. Операции Insert
, Add
и RemoveAt
будут вызываться в базовом ObservableCollection
и поэтому вызывают событие CollectionChanged
, которое должно быть запущено.
Вы также можете сказать, что просто хотите связать или получить уведомление, когда один элемент, который должен иметь доступ к изменениям. Вы снова создадите свой собственный класс, полученный из Stack или Queue, и запустите событие CollectionChanged вручную, когда:
- Что-то нажимается или выталкивается из стека
- Что-то выгружено из очереди
- Что-то поставлено в очередь в очереди, когда очередь была ранее пустой
Ответ 2
Я сталкиваюсь с той же проблемой и хочу поделиться своим решением с другими. Надеюсь, это кому-то поможет.
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; }
}
}
Ответ 3
Очень похоже на приведенный выше класс, за некоторыми исключениями:
- Публикация реквизита изменена для изменений коллекции для Count
- Переопределить TrimExcess() b/c, который может повлиять на количество
- Обнародовал события, поэтому мне не нужно приводить к интерфейсу
- При необходимости передает индекс в коллекцию
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;
}
}
Ответ 4
Я понимаю, что уже есть несколько ответов, но подумал, что я бы немного ответил с моим. Я собрал все упомянутое в постах и комментариях. Было несколько вещей, которые побудили меня сделать это:
- INPC должен всегда срабатывала для
Count
, когдаPush
,Pop
илиClear
называются, как уже упоминалось в одном из постов. - Для
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
}