2011-01-03 3 views
16

Я добавил CollectionChanged eventhandler(onCollectionChanged) к одному из объектов ObservableCollection.Осуществление CollectionChanged

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

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

Спасибо.

ответ

36

Вы должны добавить PropertyChanged слушателя к каждому элементу (который должен реализовать INotifyPropertyChanged), чтобы получить уведомление о редактировании объектов в наблюдаемом списке.

public ObservableCollection<Item> Names { get; set; } 
public List<Item> ModifiedItems { get; set; } 

public ViewModel() 
{ 
    this.ModifiedItems = new List<Item>(); 

    this.Names = new ObservableCollection<Item>(); 
    this.Names.CollectionChanged += this.OnCollectionChanged; 
} 

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
{ 
    if (e.NewItems != null) 
    { 
     foreach(Item newItem in e.NewItems) 
     { 
      ModifiedItems.Add(newItem); 

      //Add listener for each item on PropertyChanged event 
      newItem.PropertyChanged += this.OnItemPropertyChanged;   
     } 
    } 

    if (e.OldItems != null) 
    { 
     foreach(Item oldItem in e.OldItems) 
     { 
      ModifiedItems.Add(oldItem); 

      oldItem.PropertyChanged -= this.OnItemPropertyChanged; 
     } 
    } 
} 

void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    Item item = sender as Item; 
    if(item != null) 
     ModifiedItems.Add(item); 
} 

Может быть, вы должны проверить, если какой-то предмет уже в ModifedItems-List (с методом Листа Содержит (объект OBJ)) и только добавить новый элемент, если результат этого метода является ложным.

Класс Item должен реализовать INotifyPropertyChanged. См. Это example, чтобы узнать как. Как сказал Роберт Россни, вы также можете сделать это с IEditableObject - если у вас есть это требование.

+2

Это вызовет исключение для операций Add и Remove, если вы не измените кода, чтобы проверить, что e.NewItems и e.OldItems имеют значение null, прежде чем пытаться просмотреть их. – Alain

+0

Зачем вам этот список ModifiedItems, когда у вас есть коллекция имен? –

+0

См. Мое изменение выше ответа на http://stackoverflow.com/a/36398137/638977 –

1

Я думаю что Наполнение ObservableCollection с элементами, которые реализуют INotifyPropertyChanged приведет CollectionChanged событие срабатывает, когда элемент поднимает уведомление PropertyChanged.

С другой стороны, я думаю, вам нужно использовать BindingList<T>, чтобы отдельные изменения элементов распространялись таким образом из коробки.

В противном случае вам необходимо будет вручную подписаться на уведомления об изменениях каждого элемента и поднять событие CollectionChanged. Обратите внимание: если вы создаете свой собственный, полученный ObservableCollection<T>, вам придется подписаться при создании экземпляра и на Add() и Insert() и отказаться от подписки на Remove(), RemoveAt() и Clear(). В противном случае вы можете подписаться на событие CollectionChanged и использовать добавленные и удаленные элементы из аргументов событий для подписки/отмены подписки.

+3

Это верно в том, что вы не будете получать уведомления об изменениях, когда элементы в пределах изменения контейнера. –

+0

1up для упоминания BindingList. Он действительно хорошо работает с WPF и является IMHO во многих случаях лучше, чем ObservableCollection. – Riva

0

in winforms, BindingList - стандартная практика. в WPF & Silverlight, вы обычно застряла работу с ObservableCollection и нужно слушать PropertyChanged по каждому пункту

+0

Afaik BindingList на самом деле хорошо работает с WPF. – Riva

2

INotifyCollectionChanged не один в том же самом с INotiftyPropertyChanged. Изменение свойств базовых объектов никоим образом не означает, что коллекция изменилась.

Одним из способов достижения этой цели является создание пользовательской коллекции, которая будет допрашивать объект после добавления и зарегистрироваться для события INotifyPropertyChanged.PropertyChanged; тогда ему необходимо будет де-зарегистрироваться надлежащим образом, когда элемент будет удален.

Одно предостережение с таким подходом - это когда ваши объекты вложены в глубины N уровней. Чтобы решить эту проблему, вам нужно будет по существу опросить каждое свойство с помощью отражения, чтобы определить, возможно ли это еще одна коллекция, реализующая INotifyCollectionChanged или другой контейнер, который необходимо будет пройти.

Вот элементарная ООН проверенного пример ...

public class ObservableCollectionExt<T> : ObservableCollection<T> 
    { 
     public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; 

     protected override void SetItem(int index, T item) 
     { 
      base.SetItem(index, item); 

      if(item is INotifyPropertyChanged) 
       (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged); 
     } 

     protected override void ClearItems() 
     { 
      for (int i = 0; i < this.Items.Count; i++) 
       DeRegisterINotifyPropertyChanged(this.IndexOf(this.Items[i])); 

      base.ClearItems(); 
     } 

     protected override void InsertItem(int index, T item) 
     { 
      base.InsertItem(index, item); 
      RegisterINotifyPropertyChanged(item); 
     } 

     protected override void RemoveItem(int index) 
     { 
      base.RemoveItem(index); 
      DeRegisterINotifyPropertyChanged(index); 
     } 

     private void RegisterINotifyPropertyChanged(T item) 
     { 
      if (item is INotifyPropertyChanged) 
       (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged); 
     } 

     private void DeRegisterINotifyPropertyChanged(int index) 
     { 
      if (this.Items[index] is INotifyPropertyChanged) 
       (this.Items[index] as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged; 
     } 

     private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      T item = (T)sender; 
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item)); 
     } 
    } 
+0

Это не удастся. Конструктор NotifyCollectionChangedEventArgs генерирует следующее исключение, когда вы добавляете элемент в качестве параметра: 'System.ArgumentException: 'Действие Reset должно быть инициализировано без изменений.' ' – tjmoore

10

ItemsControl прослушивает CollectionChanged для управления отображением коллекции предметов, которые она представляет на экране. A ContentControl прослушивает PropertyChanged для управления отображением определенного элемента, который он представляет на экране. Очень просто удержать эти две концепции в своем уме, как только вы это поймете.

Отслеживание того, отредактирован элемент или нет, не является чем-то одним из этих интерфейсов. Изменения свойств не редактируются, т. Е. Они не обязательно представляют собой какое-то инициированное пользователем изменение состояния объекта. Например, объект может иметь свойство ElapsedTime, которое постоянно обновляется таймером; пользовательский интерфейс должен быть уведомлен об этих событиях изменения свойств, но они, конечно же, не представляют изменения в базовых данных объекта.

Стандартный способ отслеживания, был ли отредактирован объект, состоит в том, чтобы сначала реализовать этот объект IEditableObject. Затем вы можете внутренне перейти к классу объекта, решить, какие изменения составляют редактирование (т. Е. Потребовать от вас позвонить BeginEdit), а какие нет. Затем вы можете реализовать свойство boolean IsDirty, которое устанавливается, когда вызывается BeginEdit, когда вызывается EndEdit или CancelEdit. (Я действительно не понимаю, почему это свойство не входит в состав IEditableObject; я еще не выполнил редактируемый объект, который не требовал его.)

Конечно, нет необходимости реализовывать этот второй уровень абстракции, если вам это не нужно - вы можете, конечно, прослушать событие PropertyChanged и просто предположить, что объект был отредактирован, если он поднят. Это действительно зависит от ваших требований.

0

Используйте следующий код:

-my Модель:

public class IceCream: INotifyPropertyChanged 
{ 
    private int liczba; 

    public int Liczba 
    { 
     get { return liczba; } 
     set { liczba = value; 
     Zmiana("Liczba"); 
     } 
    } 

    public IceCream(){} 

//in the same class implement the below-it will be responsible for track a changes 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void Zmiana(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

И в моем классе PersonList реализовать метод, отвечающий за активное увеличение стоимости одного после нажатия кнопки в AppBarControl

async private void Add_Click(object sender, RoutedEventArgs e) 
    { 
     List<IceCream> items = new List<IceCream>(); 
     foreach (IceCream item in IceCreamList.SelectedItems) 
     { 
      int i=Flavors.IndexOf(item); 
      Flavors[i].Liczba =item.Liczba+ 1; 
      //Flavors.Remove(item); 

      //item.Liczba += 1; 

      // items.Add(item); 
      // Flavors.Add(item); 
     } 

     MessageDialog d = new MessageDialog("Zwiększono liczbę o jeden"); 
     d.Content = "Zwiększono liczbę o jeden"; 
     await d.ShowAsync(); 


     IceCreamList.SelectedIndex = -1; 
    } 
} 

Я надеюсь, что кому-то это будет полезно Обратите внимание, что:

private ObservableCollection<IceCream> Flavors; 

-

0

Самое простое решение я нашел это ограничение, чтобы удалить элемент с помощью RemoveAt(index) затем добавить измененный элемент на том же индексе с помощью InsertAt(index) и, таким образом, ObservableCollection уведомит View.

1

Мое редактирование до 'this answer' отклонено! Поэтому я кладу редактировать здесь:

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
{ 
if (e.NewItems != null) 
{ 
    foreach(Item newItem in e.NewItems) 
    { 
     ModifiedItems.Add(newItem); 

     //Add listener for each item on PropertyChanged event 
     if (e.Action == NotifyCollectionChangedAction.Add) 
      newItem.PropertyChanged += this.ListTagInfo_PropertyChanged; 
     else if (e.Action == NotifyCollectionChangedAction.Remove) 
      newItem.PropertyChanged -= this.ListTagInfo_PropertyChanged; 
    } 
} 

// MSDN: OldItems:Gets the list of items affected by a Replace, Remove, or Move action. 
//if (e.OldItems != null) <--- removed 
} 
Смежные вопросы