2011-02-08 91 views
4

检查以确保我的假设是正确的。使用INotifyPropertyChanged更新ObservableCollection项目属性

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

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

例如,在下面的情况下,没有任何PropertyChanged事件在任何Device类属性上触发。

ObservableCollection<Device> Items = new ObservableCollection<Device>(); 
Items = LoadItems(); 

List<Device> devices = GetDevices(); 

foreach (var item in Items) 
{ 
    var currentDevice = devices.Single(d1 => d1.ID == item.ID); 
    item = currentDevice; 
} 

但是,如果我手动更新每个属性,我在业务:

ObservableCollection<Device> Items = new ObservableCollection<Device>(); 
Items = LoadItems(); 

List<Device> devices = GetDevices(); 

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

在上述情况下,经度和纬度火他们的活动。

由于我的课有一堆属性有没有更好的方法来做到这一点比一个一个?

回答

1

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

public static class ExtensionMethods 
{ 
    public static void Load<T>(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并控制它。 (只需将|| !deep添加到那个long if表达式中)

注意:如果您愿意,您当然也可以覆盖您的对象引用并使用反射为所有不同的属性引发PropertyChanged事件。无论哪种方式,你不需要手动处理每个属性。

EDIT2:因为PropertyInfo.GetValue返回一个object我以前的代码没有递归加载,遗憾的是这样,你必须明确地传递一个类型,见老版本的修订。

编辑3:有关如何使这项工作没有类型参考见这dedicated question我问。然而,这并没有解决其他问题,如循环引用和枚举对象的属性。

+0

嘿,这工作得很好。我在Device中的一种类型是属性类,它是类。所以我不得不调用item.Load(currentDevice)和item.Location.Load(currentDevice.Location) – 2011-02-08 17:14:22

0

我认为你可以重载=运营商,并在那里做属性分配。那么将生成PropertyChanged事件,并且您将仍然具有与第一个示例中相同的语法。

+1

这真的很酷,但魔术师可能会吓人。 – bryanbcook 2011-02-08 14:28:12

+0

@bryanbcook哈哈可以有点..:D ..但我建议OP作为OP想要在提到的语法中使用它。这只能通过这个过载 – 2011-02-08 14:36:38

3

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

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

item.RaisePropertyChanged(""); 

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

+0

我会使用`String.Empty`,它有点干净。 – 2011-02-08 16:26:10

0

设备类必须实现接口INotifyPropertyChanged。然后为每个属性触发通知属性更改事件作​​为usuall。

这将使属性自动更改通知。

相关问题