Реализация CollectionChanged - PullRequest
       4

Реализация CollectionChanged

22 голосов
/ 04 января 2011

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

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

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

Спасибо.

Ответы [ 8 ]

45 голосов
/ 04 января 2011

Вы должны добавить слушателя 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 содержит объект (объектный объект)), и добавить новый элемент, только если результат этого метода false .

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

10 голосов
/ 04 января 2011

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

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

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

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

4 голосов
/ 04 апреля 2016

Мое изменение в ' этот ответ ' отклонено! Итак, я разместил свою правку здесь:

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
}
2 голосов
/ 04 января 2011

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)); 
        }
    }
1 голос
/ 04 января 2011

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

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

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

0 голосов
/ 27 февраля 2016

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

0 голосов
/ 27 июня 2015

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

-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 голосов
/ 04 января 2011

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

...