如何正确更新对象列表

本文关键字:列表 对象 更新 何正确 | 更新日期: 2023-09-27 18:11:06

我通过web请求获得具有某些属性的对象列表。现在我想在列表中显示那些具有属性的对象(我使用WPF ListBox和DataTemplate),并每5秒刷新一次。我知道有两种方法:

  1. 每5秒清除列表并填充新接收到的数据。这种方法会导致一些讨厌的事情,比如失去当前选择和工具提示闪烁
  2. 尝试找到匹配的元素并更新它们的属性;然后向集合中添加新元素并删除已删除的元素。这种方法保持对现有对象的引用,只是更新它们的内容,从而减少GUI的bug。但是这个更难实现

我应该使用什么方法?也许有现成的(简单的)解决方案来解决第二个问题?

看来我得澄清我的问题了。我确实使用双向绑定的ObservableCollection<T>。问题是,当我想更新我做一个web请求,并获得另一个集合与对象的不同实例。我想问的是如何将新集合的更改应用到旧集合

如何正确更新对象列表

您应该使用实现INotifyPropertyChanged的类,并将您的列表保存在ObservableCollection<>中。

对于您想绑定到接口的联系人的每个属性,确保setter在值更改时引发NotifyPropertyChanged事件,这将处理UI更新。

然后,就只剩下在集合中找到要更改的正确项了——只要每个联系人都有一些唯一的键,这应该不是那么困难,即

var contactToUpdate = myCollection.SingleOrDefault(c => c.Key == itemKey);

制作用户友好、流畅、快速的界面从来都不是一件容易的事。

因此,您可能会找到一些"中间件"解决方案,在代码的复杂性和您可以负担得起的编写时间之间取得平衡。

就像一个简单而直接的解决方案,可能对你的UX更好,你可以想到:

  • 如果使用ObservableCollection<T>进行绑定,不要直接使用它,而是将其用作派生类,其中控制何时实际触发更新事件。这样做你可以更新集合(添加/删除/更改)和触发更新事件一次所以也避免了可能的UI元素频繁闪烁,导致经常频繁的更新调用,所以绑定UI引用请求。

    例如看看:ObservableCollection不支持adrange方法,所以我得到通知的每一个项目添加,除了什么INotifyCollectionChanging?

  • selection是一个UI工件的属性,就像按钮的颜色。这样想吧。所以selection可以像一个值一样存储在你的model view中,并且在更新应用后,如果可能的话(例如,在之前选择的记录不存在的情况下),重新应用UI上的选择。

2。可能看起来很难,但就是这样做的。

重新绑定将是"华丽的"和低效的。

在你的类中,如果你重写等号,那么它就更容易找到
实现Equals方法
它的作用是让你比较不同的实例是否相等。
然后可以使用LINQ FirstOrDefault

你会认为这是打击,不确定它会为你的情况下工作,但你有没有考虑过一个哈希集支持,你暴露为一个公共IEnumerable,只是调用INPC上的公共IEnumerable后,你所有的更新。我这样做,在我使用它的地方,它工作得很好。

我决定使用第二种方法。这是我想到的。

我声明了一个属性,用于所有带有data

的属性
[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class CopyPropertyAttribute : Attribute
{
    public static void CopyProperties(object src, object dest)
    {
        // todo: cache
        var props = src.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(CopyPropertyAttribute), true).Length > 0).ToList();
        props.ForEach(p => p.SetValue(dest, p.GetValue(src, null), null));
    }
} 

然后我用以下方法将新数据合并到旧集合中

public void MergeInstances(ObservableCollection<Instance> existingInstances, IEnumerable<Instance> newInstances)
{
    var comp = new InstanceComparer();
    var itemsToRemove = existingInstances.Except(newInstances, comp).ToList();
    var itemsToAdd = newInstances.Except(existingInstances, comp).ToList();
    var itemsToUpdate = existingInstances.Join(newInstances, a => a.GetId(), a => a.GetId(), (a, b) => new { Old = a, New = b }).ToList();
    itemsToAdd.ForEach(a => existingInstances.Add(a));
    itemsToRemove.ForEach(a => existingInstances.Remove(a));
    itemsToUpdate.ForEach(a => CopyPropertyAttribute.CopyProperties(a.New, a.Old));
}

我还使用InstanceCollectionView.DeferRefresh()来优化GUI更新