ItemsSource и коллекции, в которых изменяются только свойства элементов

У меня горе с ComboBox, не отражающим изменения в свойствах коллекции, к которой привязан его ItemsSource.

Существует дерево, состоящее из видимой коллекции категорий объектов категории, содержащих объекты настройки. Некоторые настройки определяют имена презентаций для домена значений, разрешенных для других настроек. Они распределены по нескольким категориям, но небольшая магия с LINQ создает ObservableCollection, в которой ChannelCategory предоставляет свойства ChannelNumber и ChannelTitle.

ChannelCategory.ChannelTitle — это StringSetting, ChannelNumber — это int, неизменяемое значение идентификатора, для которого ChannelTitle предоставляет отображаемую строку. Опуская обильные, но не относящиеся к делу другие узлы, мы имеем следующее:

Channels
  ChannelCategory
    ChannelNumber = 1
    ChannelTitle = "caption for channel one"
  ChannelCategory
    ChannelNumber = 2
    ChannelTitle = "caption for channel two"
  ChannelCategory
    ChannelNumber = 3
    ChannelTitle = "caption for channel three"
...

Эта коллекция каналов подготавливается и предоставляется свойством класса, созданного и добавленного в словарь ресурсов окна в XAML (доступный как StaticResource). Такое расположение позволяет мне декларативно привязать поле со списком к каналам.

    <ComboBox VerticalAlignment="Center" Grid.Column="2" 
              ItemsSource="{Binding Source={StaticResource cats}, Path=Channels}"
              DisplayMemberPath="ChannelTitle.Editor"
              SelectedValuePath="ChannelNumber"
              SelectedValue="{Binding Editor}"
              />

Все это работает, но изменения значения ChannelTitle в другом месте не отражаются в значениях, отображаемых в списке со списком.

Различные приемы отладки с точками останова и событием DropDownOpened позволили мне убедиться, что обновления имеются в коллекции, на которую ссылается ItemsSource.

И, наконец, мы доходим до тайны. Почему поле со списком не обнаруживает изменения в элементах коллекции? Сама коллекция является ObservableCollection, поэтому она должна уведомлять об изменениях свойств.

Все элементы коллекции — это ChannelCategory, который является DependencyObject, а ChannelCategory.ChannelTitle — это DependencyProperty.

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

Может ли кто-нибудь предложить стратегию внесения изменений в ChannelTitle, чтобы коллекция каналов уведомляла об изменениях, чтобы поле со списком обновлялось?


Предложение Рэйчел закончилось так, как показано ниже. В контексте моего приложения было значительно больше сложности, потому что каждая ChannelCategory владеет набором объектов настроек, поэтому изменяющееся значение является свойством объекта в наборе, который является свойством объекта в наборе, к которому привязан ItemsSource. . Но суть предложения Рейчел просто нуждалась в применении на двух уровнях.

public class ObservableChannelCollection : ObservableCollection<ChannelCategory>
{
  protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  {
    if (e.NewItems != null)
      foreach (ChannelCategory channel in e.NewItems)
        channel.PropertyChanged += channel_PropertyChanged;
    if (e.OldItems != null)
      foreach (ChannelCategory channel in e.OldItems)
        channel.PropertyChanged -= channel_PropertyChanged;
    base.OnCollectionChanged(e);
  }
  void channel_PropertyChanged(object sender, PropertyChangedEventArgs e)
  {
    OnPropertyChanged(e);
  }
}

person Peter Wone    schedule 10.10.2011    source источник


Ответы (1)


ObservableCollection отслеживает изменения самой коллекции, такие как Add, Remove, Reset и т. д., но не отслеживает изменения отдельных элементов внутри коллекции. Поэтому, если вы обновите свойство элемента в ObservableCollection, коллекция не получит уведомление о том, что что-то изменилось.

Один из вариантов — добавить событие к событию ObservableCollection.CollectionChanged, а когда будут добавлены новые элементы, подключить изменение свойства к новым элементам, которое вызовет событие изменения коллекции.

void MyObservableCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach(MyItem item in e.NewItems)
        {
            MyItem.PropertyChanged += MyItem_PropertyChanged;
        }
    }

    if (e.OldItems!= null)
    {
        foreach(MyItem item in e.OldItems)
        {
            MyItem.PropertyChanged -= MyItem_PropertyChanged;
        }
    }
}

void MyItem_PropertyChanged(object sender, PropertyChange e)
{
    RaisePropertyChanged("MyObservableCollection");
}
person Rachel    schedule 10.10.2011
comment
Это в значительной степени то, что я имел в виду; Я не мог заставить мысль сформироваться, и я благодарен. Я получил класс от ObservableCollection‹ChannelCategory› и дал ему ctor и методы обработчика событий, реализующие ваше предложение, а также расширив ChannelCategories для отображения необходимого события PropertyChanged. Еще неизвестно, сработает ли наша стратегия; Я буду держать вас в курсе. - person Peter Wone; 11.10.2011
comment
Работает хорошо. Спасибо. Я не думаю, что у вас есть мысли о том, чтобы заставить XamlServices правильно сериализовать перечисления? - person Peter Wone; 20.10.2011