4

Checking to make sure my 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?

Omar Shahine
  • 1,935
  • 3
  • 22
  • 30

4 Answers4

3

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.

bryanbcook
  • 16,210
  • 2
  • 40
  • 69
1

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.

Community
  • 1
  • 1
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Hey, this worked really well. One of the type that I have in Device is a Property Location which is class. So I had to call item.Load(currentDevice) and item.Location.Load(currentDevice.Location) – Omar Shahine Feb 08 '11 at 17:14
  • My method should have loaded `Location`, too but there was a flaw in my code. See Edit2 and the new code for an update. Of course i should note that if you now only call `item.Load(item.GetType(), currentDevice, true)` that means that the `PropertyChanged` event for the `Location` property will not be fired, but depending on your code this might not be much trouble since all properties of `Location` (hopefully) will be updated. You could maybe modify the code to first assign a new object to Location, then load the properties if that is a problem. – H.B. Feb 08 '11 at 19:17
  • I asked a question about a way to get rid of the Type reference, see Edit3 in the answer. – H.B. Feb 08 '11 at 20:41
  • Glad that helped, just keep the flaws in mind that i mention in Edit3, in case you modify the classes for which you use the method. – H.B. Feb 14 '11 at 15:52
0

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.

Shekhar_Pro
  • 18,056
  • 9
  • 55
  • 79
  • @bryanbcook ha ha can be a bit.. :D.. but i suggested as OP wanted to use it in the mentioned syntax. which can only be done by this overload – Shekhar_Pro Feb 08 '11 at 14:36
0

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.

Thanigainathan
  • 1,505
  • 14
  • 25
  • He wrote: "Since my class has a bunch of properties is there a better way to do this than one by one?", even if the event firing is a bit less code than assignment it's still quite tedious. – H.B. Feb 08 '11 at 07:23
  • I already implement INotifyPropertyChanged on the Device class, hence my question – Omar Shahine Feb 08 '11 at 15:39