开发者

Updating ObservableCollection Item properties using INotifyPropertyChanged

Checking to make sure my 开发者_Python百科assumptions are correct.

I have an ObservableCollection class. I am calling a web service and retrieving an array of Devices. I am then enumerating the ObservableCollection and setting each item to the corresponding device retrieved from the web service. The devices I rerieved have different property values than the items in the ObservableCollection, but PropertyChanged events are not firing.

I assume that this is ByDesign and that in order to have the PropertyChanged event fire I actually have to enumerate each property and set the value?

For example, in the case below, no PropertyChanged events fire on any of the Device class properties.

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

However, if I manually update each property, I'm in business:

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

In the case above, both Latitude and Longitude fire their events.

Since my class has a bunch of properties is there a better way to do this than one by one?


In the first example, setting the item in the collection will fire the CollectionChanged event, not the PropertyChanged event of the individual item.

You can notify all properties by specifying an empty string to the PropertyChanged event. For example:

item.RaisePropertyChanged("");

where RaisePropertyChanged is a public method that invokes the PropertyChanged event of the INotifyPropertyChanged implementation.


For that a Load method might be useful, so you don't overwrite the reference but set all the properties of the old object instead. Here's a generic extension method i just wrote which will assign all the writeable properties, it's very rough:

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

You should then be able to call

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

to assign all the values (if they are properties).

Edit: Made the method recursive so it will call Load for properties which are not of a primitive type or value type (or string). Probably still misbehaves in some cases.
You could also add a bool deep to the method and control if it should deep-load if that might be needed. (Just add || !deep to that long if-expression)

Note: You could of course also overwrite your object reference and use reflection to raise the PropertyChanged event for all the different properties if you prefer. Either way you don't need to handle each property manually.

Edit2: Because PropertyInfo.GetValue returns an object my previous code did not load recursively, unfortunately with this you have to explicitly pass a Type, see revisions for old version.

Edit3: For ways to make this work without the Type reference see this dedicated question that i asked. This however does not address other issues like circular references and properties that enumerate objects.


I think you can overload = operator and do the assignment of properties there. then the PropertyChanged events will be generated and you will still have the same syntax as in first example.


The Device class have to implement the interface INotifyPropertyChanged .Then for each property fire the notify property changed event as usuall.

This will enable the property changed notification automatically.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜