Обновление свойств элемента ObservableCollection с использованием INotifyPropertyChanged - PullRequest
4 голосов
/ 08 февраля 2011

Проверка правильности моих предположений.

У меня есть класс ObservableCollection. Я звоню в веб-сервис и получаю массив устройств. Затем я перечисляю ObservableCollection и устанавливаю каждый элемент на соответствующее устройство, полученное из веб-службы. Устройства, которые я получил, имеют значения свойств, отличающиеся от элементов в 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;
}

В приведенном выше случае и Широта, и Долгота запускают свои события.

Поскольку у моего класса есть куча свойств, есть ли лучший способ сделать это, чем одно за другим?

Ответы [ 4 ]

3 голосов
/ 08 февраля 2011

В первом примере установка элемента в коллекции вызовет событие CollectionChanged, а не событие PropertyChanged отдельного элемента.

Вы можете уведомить все свойства, указав пустую строку для события PropertyChanged.Например:

item.RaisePropertyChanged("");

, где RaisePropertyChanged - это открытый метод, который вызывает событие PropertyChanged реализации INotifyPropertyChanged.

1 голос
/ 08 февраля 2011

Для этого может быть полезен метод 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)

Примечание. Конечно, вы также можете перезаписать ссылку на свой объект и использовать отражение, чтобы вызвать событие PropertyChanged для всех различных свойств, если хотите. В любом случае вам не нужно обрабатывать каждое свойство вручную.

Edit2: Поскольку PropertyInfo.GetValue возвращает object, мой предыдущий код не загружался рекурсивно, к сожалению, с этим вы должны явно передать Type, см. Ревизии для старой версии.

Edit3: Чтобы узнать, как сделать эту работу без ссылки на тип, см. Этот специальный вопрос , который я задал. Это, однако, не решает другие проблемы, такие как циклические ссылки и свойства, которые перечисляют объекты.

0 голосов
/ 08 февраля 2011

Класс Device должен реализовывать интерфейс INotifyPropertyChanged. Затем для каждого свойства срабатывает событие уведомления об изменении свойства как обычное.

Это автоматически включит уведомление об изменении свойства.

0 голосов
/ 08 февраля 2011

Думаю, вы можете перегрузить оператор = и сделать там присвоение свойств.тогда будут генерироваться события PropertyChanged, и вы по-прежнему будете иметь тот же синтаксис, что и в первом примере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...