使用INotifyPropertyChanged更新ObservableCollection项属性

检查以确保我的假设是正确的。

我有一个ObservableCollection类。 我正在调用Web服务并检索一组设备。 然后我枚举ObservableCollection并将每个项目设置为从Web服务检索到的相应设备。 我所感到的设备具有与ObservableCollection中的项不同的属性值,但PropertyChanged事件未触发。

我假设这是ByDesign,为了让PropertyChanged事件触发,我实际上必须枚举每个属性并设置值?

例如,在下面的情况中,任何Device类属性都不会触发PropertyChanged事件。

ObservableCollection Items = new ObservableCollection(); Items = LoadItems(); List devices = GetDevices(); foreach (var item in Items) { var currentDevice = devices.Single(d1 => d1.ID == item.ID); item = currentDevice; } 

但是,如果我手动更新每个属性,我就是在做生意:

 ObservableCollection Items = new ObservableCollection(); Items = LoadItems(); List devices = GetDevices(); foreach (var item in Items) { var currentDevice = devices.Single(d1 => d1.ID == item.ID); item.Latitude = currentDevice.Latitude; item.Longitude= currentDevice.Longitude; } 

在上面的例子中,纬度和经度都会触发他们的事件。

由于我的课程有很多属性,有没有比一个一个更好的方法呢?

为此,Load方法可能很有用,因此您不会覆盖引用,而是设置旧对象的所有属性。 这是我刚刚编写的一个通用扩展方法,它将分配所有可写属性,它非常粗糙:

 public static class ExtensionMethods { public static void Load(this T target, Type type, T source, bool deep) { foreach (PropertyInfo property in type.GetProperties()) { if (property.CanWrite && property.CanRead) { if (!deep || property.PropertyType.IsPrimitive || property.PropertyType == typeof(String)) { property.SetValue(target, property.GetValue(source, null), null); } else { object targetPropertyReference = property.GetValue(target, null); targetPropertyReference.Load(targetPropertyReference.GetType(), property.GetValue(source, null), deep); } } } } } 

然后你应该可以打电话

 item.Load(item.GetType(), currentDevice, true); //false for shallow loading 

分配所有值(如果它们是属性)。

编辑:使方法递归,因此它将为不属于基本类型或值类型(或字符串)的属性调用Load 。 在某些情况下可能仍然行为不端。
您还可以在方法中添加一个bool,并控制是否应该深度加载(如果可能需要)。 (只需添加|| !deep到那个长if-expression)

注意:您当然也可以覆盖对象引用并使用reflection来提升所有不同属性的PropertyChanged事件(如果您愿意)。 无论哪种方式,您都不需要手动处理每个属性。

Edit2:因为PropertyInfo.GetValue返回一个object我以前的代码没有递归加载,不幸的是,你必须显式传递一个Type,请参阅旧版本的修订版。

编辑3:如果没有类型参考使这项工作的方法看到我问的这个专门的问题 。 但是,这并未解决其他问题,如循环引用和枚举对象的属性。

在第一个示例中,设置集合中的项目将触发CollectionChanged事件,而不是单个项目的PropertyChanged事件。

您可以通过为PropertyChanged事件指定空字符串来通知所有属性。 例如:

 item.RaisePropertyChanged(""); 

其中RaisePropertyChanged是一个调用INotifyPropertyChanged实现的PropertyChanged事件的公共方法。

我认为你可以重载=运算符并在那里进行属性赋值。 然后将生成PropertyChanged事件,您仍将使用与第一个示例中相同的语法。

Device类必须实现接口INotifyPropertyChanged。然后为每个属性触发notify属性更改事件asuall。

这将自动启用属性更改通知。