【问题标题】:How do I update a single item in an ObservableCollection class?如何更新 ObservableCollection 类中的单个项目?
【发布时间】:2011-07-21 18:54:32
【问题描述】:

如何更新 ObservableCollection 类中的单个项目?

我知道如何添加。而且我知道如何在“for”循环中一次搜索 ObservableCollection 一个项目(使用 Count 作为项目数量的表示),但我如何更改现有项目。如果我执行“foreach”并找到需要更新的项目,如何将其放回 ObservableCollection>

【问题讨论】:

  • 您的意思是“删除一个项目并将另一个项目放在它的位置”,还是只是“更新一个项目的属性并让更改触发事件以更新我的 UI”?
  • @NeilBarnwell 不是 OP,但当我更改列表中已有项目的属性时,我需要更新 UI。
  • 你只需要每个item实现IObservable

标签: c# .net silverlight linq


【解决方案1】:

您无需删除项目、更改然后添加。您可以简单地使用 LINQ FirstOrDefault 方法使用适当的谓词找到必要的项目并更改它的属性,例如:

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

删除或添加项目到ObservableCollection 将产生CollectionChanged 事件。

【讨论】:

  • 你能用两个测试把它加倍吗,比如 (i => (i.Name == "John") && (i.Street == "Main")) ?
  • @xarzu lambda => 之后的所有内容都被评估为布尔值。 LINQ 是一种处理集合的强大方法,值得花一些时间学习可用的不同方法
  • 这会为我更新集合但不更新 UI?还有什么我应该做的吗?
  • @jay_t55 你是说我需要用当前的项目来源替换一个新的吗?一个完全相同,但一个成员的单个字段发生了变化?乍一看似乎很浪费。特别是考虑到当我们在其中添加或删除对象时,可观察集合能够检测并自动更新到 GUI...
  • @AhmedMohammed 如果列表项是引用类型,则将更新实际项。
【解决方案2】:

您通常无法更改您正在迭代的集合(使用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 并确保引发@987654330 @ 当您要更改的属性发生更改时(理想情况下,如果您有接口,它应该在所有公共属性上实现,但在功能上当然只对您将要更新的属性很重要)。

【讨论】:

  • 实施 INotifyPropertyChanged 为我解决了更新问题
  • 第 2 点对我来说很有趣。我在做found = newObject,但那行不通。必须意味着FirstOrDefault 返回它找到的对象的重复实例。您使用 IndexOf 让 REAL 实例为我工作的解决方案
  • @user1 您误解了引用对象的含义。 FirstOrDefault 复制 instance 本身。如果您修改该实例上的属性,它会随处更改它。局部变量found 只是一个引用,例如,“实例位于[内存位置] 123”。该集合还有一些内容还说“实例位于 123”。如果您将found 更改为“实例位于 434”,则集合中仍有“实例位于 123”的内容。
【解决方案3】:

这里有 Tim S's examples 作为 Collection 类之上的扩展方法:

CS 与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

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  

VB 与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

【讨论】:

    【解决方案4】:

    这取决于它是什么类型的对象。

    如果是普通的 C# 类,只需更改对象的属性即可。您无需对集合执行任何操作。集合包含对对象的引用,即使对象的属性发生更改,该引用也是相同的。对象的更改不会触发集合本身的更改通知,因为集合并没有真正改变,只是其中的一个对象。

    如果它是不可变的 C# 类(例如字符串)、struct 或其他值类型,则必须删除旧值并添加新值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-09
      • 2014-01-29
      • 1970-01-01
      • 2010-11-24
      • 2012-07-04
      • 2011-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多