Реализация 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.