I have a ObservableCollection
, I can add and remove item from the collection. But I can't replace an existing item in the collection. There is a way to replace an item and reflect that on my bound开发者_StackOverflow components.
System.Collections.Specialized.NotifyCollectionChangedAction.Replace
Can anyone please show me how to accomplish this?
collection[someIndex] = newItem;
Updated: Indexer uses overridden SetItem and notifies about changes.
I think the answer about using indexer may be wrong, because the question was about replace and notify.
Just to clarify: ObservableCollection<T>
uses indexer from its base Collection<T>
class, which in turn is a wrapper of List<T>
, which is a wrapper of simple array of T
. And there's no override for indexer method in ObservableCollection implementation.
So when you use indexer to replace an item in ObservableCollection it invokes the following code from Collection class:
public T this[int index] {
get { return items[index]; }
set {
if( items.IsReadOnly) {
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
}
if (index < 0 || index >= items.Count) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
SetItem(index, value);
}
It just checks boundaries and calls SetItem that uses indexer of underlying List class:
protected virtual void SetItem(int index, T item) {
items[index] = item;
}
During assignment there is no call to the CollectionChanged
event, because underlying collections know nothing of it.
But when you use SetItem
method, it is called from ObservableCollection class:
protected override void SetItem(int index, T item)
{
CheckReentrancy();
T originalItem = this[index];
base.SetItem(index, item);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
}
After assignment it calls OnCollectionChanged
method, which fires CollectionChanged
event with NotifyCollectionChangedAction.Replace
action parameter.
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
using (BlockReentrancy())
{
CollectionChanged(this, e);
}
}
}
As a conclusion: the idea with custom class inherited from ObservableCollection and Replace
method that calls base.SetItem()
worth a try.
Simple extension method for replace item in observable collection:
public static void ReplaceItem<T>(this ObservableCollection<T> items, Func<T, bool> predicate, T newItem)
{
for (int i = 0; i < items.Count; i++)
{
if (predicate(items[i]))
{
items[i] = newItem;
break;
}
}
}
精彩评论