WPF ListBox не привязывается к событиям INotifyCollectionChanged или INotifyPropertyChanged

У меня есть следующий тестовый код:

private class SomeItem
    {
       public string Title{ get{ return "something"; } }
       public bool Completed { get { return false; } set { } }
    }

    private class SomeCollection : IEnumerable<SomeItem>, INotifyCollectionChanged
    {
        private IList<SomeItem> _items = new List<SomeItem>();
        public void Add(SomeItem item)
        {
            _items.Add(item);
            CollectionChanged(this, new 
             NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        #region IEnumerable<SomeItem> Members

        public IEnumerator<SomeItem> GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion
    }

    private SomeCollection collection = new SomeCollection();

    private void Expander_Expanded(object sender, RoutedEventArgs e)
    {
        var expander = (Expander) sender;
        var list = expander.DataContext as ITaskList;
        var listBox = (ListBox)expander.Content;
        //list.Tasks.CollectionChanged += CollectionChanged;
        collection.Add(new SomeItem());
        collection.Add(new SomeItem());
        listBox.ItemsSource = collection;
    }

и XAML

<ListBox Name="taskListList" ItemsSource="{Binding}" BorderThickness="0" ItemContainerStyle="{StaticResource noSelectedStyle}" >
    <ListBox.ItemTemplate>
        <DataTemplate>
           <Expander Expanded="Expander_Expanded">
              <Expander.Header>
                <StackPanel Orientation="Horizontal">
                  <TextBlock Text="{Binding Name}" />
                  <TextBox KeyUp="TextBox_KeyUp" Width="200"/>
                  <Button Name="hide" Click="hide_Click">
                      <TextBlock Text="hide" />
                  </Button>
                </StackPanel>
               </Expander.Header>
                  <ListBox Name="taskList" ItemsSource="{Binding}" ItemTemplate=" 
                     {StaticResource taskItem}" />
              </Expander>
         </DataTemplate>
      </ListBox.ItemTemplate>
   </ListBox>

внешний список заполняется при загрузке. когда расширитель расширяется, я затем устанавливаю свойство ItemsSource внутреннего списка (причина, по которой я слышу это вместо использования привязки, заключается в том, что эта операция довольно медленная, и я хочу, чтобы она выполнялась только в том случае, если использование решит просмотреть элементы). Внутренний список отображается нормально, но на самом деле он не подписывается на событие CollectionChanged в коллекции. Я пробовал это с ICollection вместо IEnumerable и добавлял INotifyPropertyChanged, а также заменял INotifyCollectionChanged на INotifyPropertyChanged. Единственный способ заставить это работать — выпотрошить мой класс SomeCollection и наследовать от ObservableCollection<SomeItem>. Причина, по которой я пытаюсь использовать свою собственную роль INotifyCollectionChanged вместо использования ObservableCollection, заключается в том, что я оборачиваю коллекцию COM в реальный код. Эта коллекция будет уведомлять о добавлении/изменении/удалении, и я пытаюсь преобразовать их в события INotify для WPF.

Надеюсь, это достаточно ясно (уже поздно).


person Community    schedule 24.07.2009    source источник
comment
Я предполагаю, что это потому, что вы недостаточно реализуете поведение INotifyCollectionChanged и вообще не реализуете INotifyPropertyChanged. Вероятно, не стоит исследовать, чтобы найти конкретные причины, поскольку вам лучше просто изменить SomeCollection, чтобы он наследовал от ObservableCollection<T>, и многое сделать за вас. Зачем изобретать велосипед? Вы по-прежнему сможете переопределять методы, чтобы настроить его поведение, если вам нужно.   -  person Sean Hanley    schedule 30.12.2011
comment
Есть ли у вас более подробная информация о коллекции COM, которую вы пытаетесь обернуть? Я думаю, вы должны спросить, как преобразовать вашу COM-коллекцию в ObservableCollection.   -  person Iain    schedule 29.11.2013


Ответы (2)


ObservableCollection<T> также реализует INotifyPropertyChanged. Поскольку ваша коллекция — это просто IEnumerable<T>, у вас нет свойств для создания событий, но ObservableCollection<T> создаются PropertyChanged события для свойств Count и Item[]. Вы можете попытаться сделать свою коллекцию более похожей на ObservableCollection<T>, производя от IList<T> и реализуя INotifyPropertyChanged. Я не знаю, решит ли это вашу проблему.

person Martin Liversage    schedule 24.07.2009
comment
Если я правильно понимаю вопрос, коллекция уже существует и имеет базовый класс, отличный от ObserverableCollection‹T›. Эта коллекция, несмотря на то, что она реализует INotifyCollectionChanged, не требует принудительного обновления списка при его изменении. Вопрос в том, почему? - person Martin Liversage; 24.07.2009

Я не понимаю, почему я продолжаю видеть людей, пытающихся реализовать свои собственные коллекции в WPF. Просто используйте ObservableCollection<SomeItem>, и обо всех ваших уведомлениях CollectionChanged позаботятся.

private ObservableCollection<SomeItem> collection = 
    new ObservableCollection<SomeItem>();

Если вы хотите, чтобы что-то произошло SomeItem.PropertyChanged, заставьте SomeItem реализовать INotifyPropertyChanged

Что касается того, почему ваш CollectionChanged не поднимается, вы устанавливаете свойство ItemsSource, а не привязываете его. Установка этого параметра означает, что вы делаете копию collection и сохраняете ее в ListBox.ItemsSource. Связывание означает, что вы говорите ListBox.ItemsSource обращаться к collection за его данными.

person Rachel    schedule 30.12.2011
comment
ну, ObservableCollection не реализует сортировку. Поэтому, если вы хотите, чтобы ваши данные были отсортированы, вам нужно расширить какую-то другую коллекцию и реализовать INotifyCollectionChanged. - person Adarsha; 08.02.2012
comment
@Adarsha Я просто расширяю ObservableCollection<T>, чтобы включить метод Sort. Я не создаю класс, который наследуется от ObservableCollection<SomeItem> для такого поведения. Вы можете увидеть пример кода сортировки в этом ответе - person Rachel; 08.02.2012
comment
обратите внимание, что OP не расширял ObservableCollection‹SomeItem›, вместо этого он пытался реализовать INotifyCollectionChanged. Ожидается, что вы реализуете OnCollectionChanged. Обратите внимание, что ObservableCollection.Move доступен не во всех версиях .NET, например. Windows Phone. Итак, чтобы реализовать сортировку, нам нужно начать заново изобретать колесо для сортировки. Либо используйте класс, реализующий IList‹T›, INotifyCollectionChanged, либо используйте ObservableCollection и заново включите сортировку с нуля. - person Adarsha; 09.02.2012