web-dev-qa-db-ja.com

ObservableCollectionクラスの単一のアイテムを更新するにはどうすればよいですか?

ObservableCollectionクラスの単一のアイテムを更新するにはどうすればよいですか?

追加方法を知っています。そして、「for」ループで一度に1項目ずつObservableCollectionを検索する方法(項目のamoutの表現としてCountを使用)を知っていますが、既存の項目を変更するにはどうすればよいですか。 「foreach」を実行して、更新が必要なアイテムを見つけたら、それをObservableCollectionに戻す方法

35
xarzu

アイテムを削除し、変更してから追加する必要はありません。 LINQ FirstOrDefaultメソッドを使用するだけで、適切な述語を使用して必要なアイテムを検索し、プロパティを変更できます。例:

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

ObservableCollectionに対してアイテムを削除または追加すると、CollectionChangedイベントが生成されます。

37

通常、繰り返し処理するコレクションを(foreachを使用して)変更することはできません。これを回避する方法は、もちろん、変更するときに繰り返し処理しないことです。 (x.Id == myIdおよびLINQ FirstOrDefaultは、条件/検索のプレースホルダーです。重要な部分は、オブジェクトおよび/またはオブジェクトのインデックスを取得していることです)

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

または

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

または

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

または

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

最後の例で問題が解決する場合、ObservableCollectionが変化を認識していることを確認する方法が本当に必要な場合は、オブジェクトのクラスにINotifyPropertyChangedを実装し、変更するプロパティが変更されたときにPropertyChangedを発生させます(インターフェイスがある場合はすべてのパブリックプロパティに実装するのが理想的ですが、機能的にはもちろん、実際に更新するのは重要です)。

33
Tim S.

ここに Tim Sの例 がCollectionクラスの上に拡張メソッドとしてあります:

FirstOrDefaultを含むCS

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

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;
        }
    }
}

使用法

このクラスのセットアップがあると想像してください

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

次のような関数/実装のいずれかを呼び出すことができます。この場合、matchパラメーターを使用して、置換するアイテムを識別します。

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

<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  

FirstOrDefaultを含むVB

<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
5
KyleMit

これは、オブジェクトの種類によって異なります。

通常のC#クラスの場合は、オブジェクトのプロパティを変更するだけです。コレクションに対して何もする必要はありません。コレクションは、オブジェクトのプロパティが変更された場合でも同じオブジェクトへの参照を保持します。オブジェクトの変更は、コレクション自体の変更通知をトリガーしません。これは、コレクションが実際には変更されておらず、その中のオブジェクトの1つだけが変更されているためです。

不変のC#クラス(文字列など)、structまたは別の値型の場合、古い値を削除して新しい値を追加する必要があります。

2
Anders Abel