如何正确更新对象列表
本文关键字:列表 对象 更新 何正确 | 更新日期: 2023-09-27 18:11:06
我通过web请求获得具有某些属性的对象列表。现在我想在列表中显示那些具有属性的对象(我使用WPF ListBox和DataTemplate),并每5秒刷新一次。我知道有两种方法:
- 每5秒清除列表并填充新接收到的数据。这种方法会导致一些讨厌的事情,比如失去当前选择和工具提示闪烁
- 尝试找到匹配的元素并更新它们的属性;然后向集合中添加新元素并删除已删除的元素。这种方法保持对现有对象的引用,只是更新它们的内容,从而减少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更新