Реализация CollectionChanged
Я добавил CollectionChanged eventhandler(onCollectionChanged)
в одно из свойств ObservableCollection
.
Я обнаружил, что метод onCollectionChanged
вызывается только в случае добавления элементов или удаления элементов в коллекцию, но не в случае редактирования элемента коллекции.
Я хотел бы знать, как отправить список/коллекцию вновь добавленных, удаленных и отредактированных элементов в одной коллекции.
Спасибо.
Ответы
Ответ 1
Вы должны добавить слушателя 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 (со списком метода Contains (object obj)) и добавлять только новый элемент, если результатом этого метода является false.
Класс Item
должен реализовать INotifyPropertyChanged
. См. Этот пример, чтобы узнать, как это сделать. Как сказал Роберт Россни, вы также можете сделать это с помощью IEditableObject
- если у вас есть это требование.
Ответ 2
An ItemsControl
прослушивает CollectionChanged
для управления отображением коллекции элементов, которые она представляет на экране. A ContentControl
прослушивает PropertyChanged
для управления отображением определенного элемента, который он представляет на экране. Очень просто сохранить эти два понятия в своем уме, как только вы это поймете.
Отслеживание того, отредактирован элемент или нет, не является чем-то одним из этих интерфейсов. Изменения свойств не редактируются, т.е. Они не обязательно представляют собой какое-то инициированное пользователем изменение состояния объекта. Например, объект может иметь свойство ElapsedTime
, которое постоянно обновляется таймером; пользовательский интерфейс должен быть уведомлен об этих событиях изменения свойств, но они, конечно же, не представляют изменения в базовых данных объекта.
Стандартный способ отслеживать, редактирован ли объект, - это сначала реализовать этот объект IEditableObject
. Затем вы можете внутренне относиться к классу объектов, решать, какие изменения составляют редактирование (т.е. Требуют вызова BeginEdit
), а какие нет. Затем вы можете реализовать свойство boolean IsDirty
, которое устанавливается, когда BeginEdit
вызывается и очищается при вызове EndEdit
или CancelEdit
. (Я действительно не понимаю, почему это свойство не является частью IEditableObject
, я еще не реализовал редактируемый объект, который этого не требовал.)
Конечно, нет необходимости реализовывать этот второй уровень абстракции, если он вам не нужен - вы можете прослушивать событие PropertyChanged
и просто предполагать, что объект был отредактирован, если он поднят. Это действительно зависит от ваших требований.
Ответ 3
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));
}
}
Ответ 4
Я думаю, что заполнение ObservableCollection
элементами, которые реализуют INotifyPropertyChanged
, приведет к срабатыванию события CollectionChanged
, когда элемент получает уведомление PropertyChanged
.
Во-вторых, я думаю, вам нужно использовать BindingList<T>
, чтобы отдельные изменения элементов распространялись таким образом из коробки.
В противном случае вам нужно будет вручную подписаться на уведомления об изменении элемента и поднять событие CollectionChanged
. Обратите внимание: если вы создаете свой собственный, полученный ObservableCollection<T>
, вам придется подписаться при создании экземпляра и на Add()
и Insert()
и отказаться от подписки на Remove()
, RemoveAt()
и Clear()
. В противном случае вы можете подписаться на событие CollectionChanged
и использовать добавленные и удаленные элементы из аргументов событий для подписки/отмены подписки.
Ответ 5
Мое редактирование на этот ответ 'отклонено!
Поэтому я разместил здесь свое редактирование:
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
}
Ответ 6
в winforms, BindingList
- стандартная практика. в WPF и Silverlight вы обычно работаете с ObservableCollection
и должны прослушивать PropertyChanged
для каждого элемента
Ответ 7
Используйте следующий код:
-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;
-
Ответ 8
Самое простое решение, которое я нашел для этого ограничения: удалить элемент с помощью RemoveAt(index)
, затем добавить измененный элемент в тот же индекс, используя InsertAt(index)
, и, таким образом, ObservableCollection уведомит View.