Свойство зависимости ObservableCollection не обновляется при удалении элемента в коллекции

У меня есть прикрепленное свойство типа ObservableCollection на элемент управления. Если я добавляю или удаляю элементы из коллекции, пользовательский интерфейс не обновляется. Однако, если я заменю коллекцию внутри новой ViewModel, пользовательский интерфейс обновится.

Может ли кто-нибудь дать мне пример того, что мне нужно делать в объекте Dependency, чтобы он мог обрабатывать изменения в коллекции?

Часть объекта зависимости указана ниже:

public class RadCalendarBehavior : DependencyObject
{
private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  var calendar = d as RadCalendar;
  if (e.NewValue != null)
  {
    calendar.DayTemplateSelector = new SpecialDaySelector((ObservableCollection<DateTime>)e.NewValue, GetSpecialDayTemplate(d));
  }
}

public static ObservableCollection<DateTime> GetSpecialDays(DependencyObject obj)
{
  return (ObservableCollection<DateTime>)obj.GetValue(SpecialDaysProperty);
}

public static void SetSpecialDays(DependencyObject obj, ObservableCollection<DateTime> value)
{
  obj.SetValue(SpecialDaysProperty, value);
}

public static readonly DependencyProperty SpecialDaysProperty =
    DependencyProperty.RegisterAttached("SpecialDays", typeof(ObservableCollection<DateTime>), typeof(RadCalendarBehavior), new UIPropertyMetadata(null, OnSpecialDaysChanged));
}
}

Я понимаю, что мне нужно зарегистрировать, что коллекция изменилась, но я не уверен, как это сделать в свойстве зависимости

3 ответа

Решение

Изменение в коллекции не вызовет OnSpecialDaysChanged обратный вызов, потому что значение свойства зависимости не изменилось. Если вам нужно реагировать на обнаружение изменений в коллекции, вам нужно обработать событие CollectionChanged событие вручную:

private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  var calendar = d as RadCalendar;

  if (e.OldValue != null)
  {
    var coll = (INotifyCollectionChanged)e.OldValue;
    // Unsubscribe from CollectionChanged on the old collection
    coll.CollectionChanged -= SpecialDays_CollectionChanged;
  }

  if (e.NewValue != null)
  {
    var coll = (ObservableCollection<DateTime>)e.NewValue;
    calendar.DayTemplateSelector = new SpecialDaySelector(coll, GetSpecialDayTemplate(d));
    // Subscribe to CollectionChanged on the new collection
    coll.CollectionChanged += SpecialDays_CollectionChanged;
  }
}

private static void SpecialDays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // handle CollectionChanged
}

Если у вас есть свойство зависимости типа коллекции, имейте в виду следующее:

Если ваше свойство является ссылочным типом, значение по умолчанию, указанное в метаданных свойства зависимостей, не является значением по умолчанию для экземпляра; вместо этого это значение по умолчанию, которое применяется ко всем экземплярам типа. [...]
Чтобы исправить эту проблему, необходимо сбросить значение свойства зависимости коллекции на уникальный экземпляр, как часть вызова конструктора класса.

(см. Свойства зависимости типа коллекции MSDN)

Чтобы ответить на вопрос Сэма (я просто столкнулся с той же проблемой):

Сделайте ваш обработчик CollectionChanged нестатичным и отмените подписку / повторную подписку на уровне экземпляра.

private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  var calendar = d as RadCalendar;

  if (e.OldValue != null)
  {
    var coll = (INotifyCollectionChanged)e.OldValue;
    // Unsubscribe from CollectionChanged on the old collection of the DP-instance (!)
    coll.CollectionChanged -= d.SpecialDays_CollectionChanged;
  }

  if (e.NewValue != null)
  {
    var coll = (ObservableCollection<DateTime>)e.NewValue;
    calendar.DayTemplateSelector = new SpecialDaySelector(coll, GetSpecialDayTemplate(d));
    // Subscribe to CollectionChanged on the new collection of the DP-instance (!)
    coll.CollectionChanged += d.SpecialDays_CollectionChanged;
  }
}

private void SpecialDays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // handle CollectionChanged on instance-level
}

Это просто добавить к ответу Томаса. В моем коде я взаимодействую со свойствами DependencyObject, создавая объект обработчика локально, как показано ниже:

private static void OnSpecialDaysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var action = new NotifyCollectionChangedEventHandler(
            (o, args) =>
                {
                    var calendar = d as RadCalendar;

                    if (calendar!= null)
                    {
                        // play with calendar's properties/methods
                    }
                });

    if (e.OldValue != null)
    {
       var coll = (INotifyCollectionChanged)e.OldValue;
       // Unsubscribe from CollectionChanged on the old collection
       coll.CollectionChanged -= action;
    }

    if (e.NewValue != null)
    {
       var coll = (ObservableCollection<DateTime>)e.NewValue;
       // Subscribe to CollectionChanged on the new collection
       coll.CollectionChanged += action;
    }
}

Надеюсь, это кому-нибудь пригодится.

Другие вопросы по тегам