使用INotifyPropertyChanged更新ObservableCollection项属性

时间:2011-02-08 05:03:43

标签: c# silverlight windows-phone-7

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

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

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

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

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;
}

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

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

4 个答案:

答案 0 :(得分:3)

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

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

item.RaisePropertyChanged("");

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

答案 1 :(得分: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添加到那个长if-expression)

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

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

编辑3:有关在没有“类型”引用的情况下使其工作的方法,请参阅我提出的dedicated question。但是,这并未解决其他问题,如循环引用和枚举对象的属性。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

Device类必须实现接口INotifyPropertyChanged。然后,对于每个属性,将notify属性更改为eventuall事件。

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