How do I update a single item in an ObservableCollection class?

You can't generally change a collection that you're iterating through (with foreach). The way around this is to not be iterating through it when you change it, of course. (x.Id == myId and the LINQ FirstOrDefault are placeholders for your criteria/search, the important part is that you've got the object and/or index of the object)

for (int i = 0; i < theCollection.Count; i++) {
  if (theCollection[i].Id == myId)
    theCollection[i] = newObject;
}

Or

var found = theCollection.FirstOrDefault(x=>x.Id == myId);
int i = theCollection.IndexOf(found);
theCollection[i] = newObject;

Or

var found = theCollection.FirstOrDefault(x=>x.Id == myId);
theCollection.Remove(found);
theCollection.Add(newObject);

Or

var found = theCollection.FirstOrDefault(x=>x.Id == myId);
found.SomeProperty = newValue;

If the last example will do, and what you really need to know is how to make things watching your ObservableCollection be aware of the change, you should implement INotifyPropertyChanged on the object's class and be sure to raise PropertyChanged when the property you're changing changes (ideally it should be implemented on all public properties if you have the interface, but functionally of course it really only matters for ones you'll be updating).


You don't need to remove item, change, then add. You can simply use LINQ FirstOrDefault method to find necessary item using appropriate predicate and change it properties, e.g.:

var item = list.FirstOrDefault(i => i.Name == "John");
if (item != null)
{
    item.LastName = "Smith";
}

Removing or adding item to ObservableCollection will generate CollectionChanged event.


Here are Tim S's examples as extension methods on top of the Collection Class:

CS with FirstOrDefault

public static void ReplaceItem<T>(this Collection<T> col, Func<T, bool> match, T newItem)
{
    var oldItem = col.FirstOrDefault(i => match(i));
    var oldIndex = col.IndexOf(oldItem);
    col[oldIndex] = newItem;
}

CS with Indexed Loop

public static void ReplaceItem<T>(this Collection<T> col, Func<T, bool> match, T newItem)
{
    for (int i = 0; i <= col.Count - 1; i++)
    {
        if (match(col[i]))
        {
            col[i] = newItem;
            break;
        }
    }
}

Usage

Imagine you have this class setup

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

You can call either of the following functions/implementations like this where the match parameter is used to identify the item you'd like to replace:

var people = new Collection<Person>
{
    new Person() { Id = 1, Name = "Kyle"},
    new Person() { Id = 2, Name = "Mit"}
};

people.ReplaceItem(x => x.Id == 2, new Person() { Id = 3, Name = "New Person" });

VB with Indexed Loop

<Extension()>
Public Sub ReplaceItem(Of T)(col As Collection(Of T), match As Func(Of T, Boolean), newItem As T)
    For i = 0 To col.Count - 1
        If match(col(i)) Then
            col(i) = newItem
            Exit For
        End If
    Next
End Sub  

VB with FirstOrDefault

<Extension()>
Public Sub ReplaceItem(Of T)(col As Collection(Of T), match As Func(Of T, Boolean), newItem As T)
    Dim oldItem = col.FirstOrDefault(Function(i) match(i))
    Dim oldIndex = col.IndexOf(oldItem)
    col(oldIndex) = newItem      
End Sub