Implementing CollectionChanged

Dodałem CollectionChanged eventhandler(onCollectionChanged) do jednej z właściwości ObservableCollection.

Dowiedziałem się, że metoda onCollectionChanged jest wywoływana tylko w przypadku dodawania elementów lub usuwania elementów do kolekcji, ale nie w przypadku edycji elementu kolekcji.

Chciałbym wiedzieć, jak wysłać listę / kolekcję nowo dodanych, usuniętych i edytowanych elementów w jednej kolekcji.

Dzięki.
Author: Harshal Doshi Jain, 2011-01-04

8 answers

Musisz dodać PropertyChanged listener do każdego elementu (który musi zaimplementować INotifyPropertyChanged), aby otrzymać powiadomienie o edycji obiektów na liście obserwowalnej.

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);
}

być może trzeba sprawdzić, czy jakiś element jest już w ModifedItems-List (z metodą list Contains(object obj)) i dodać nowy element tylko wtedy, gdy wynikiem tej metody jest false.

Klasa {[3] } musi zaimplementować INotifyPropertyChanged. Zobacz ten przykład {[16] } aby wiedzieć jak. Jak powiedział Robert Rossney, możesz także zrób to z IEditableObject - Jeśli masz taki wymóg.

 43
Author: Arxisos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-23 10:04:20

An ItemsControl nasłuchuje CollectionChanged, Aby zarządzać wyświetlaniem kolekcji przedmiotów prezentowanych na ekranie. A ContentControl nasłuchuje PropertyChanged, aby zarządzać wyświetlaniem określonego elementu, który jest prezentowany na ekranie. Całkiem łatwo jest utrzymać te dwa pojęcia osobno w umyśle, gdy to zrozumiesz.

Śledzenie tego, czy dany element jest edytowany, nie jest czymś, co robi żaden z tych interfejsów. Zmiany nieruchomości nie są edycjami - czyli niekoniecznie stanowią jakiś zainicjowanej przez użytkownika zmiany stanu obiektu. Na przykład, obiekt może mieć właściwość ElapsedTime, która jest stale aktualizowana przez timer; interfejs użytkownika musi być powiadamiany o tych zdarzeniach zmiany właściwości, ale na pewno nie reprezentują one zmian w podstawowych danych obiektu.

Standardowy sposób śledzenia tego, czy obiekt jest edytowany, polega na tym, aby najpierw zaimplementować ten obiekt IEditableObject. Możesz wtedy, wewnętrznie do klasy obiektu, zdecydować, jakie zmiany stanowią edycję (tzn. wymagaj wywołania BeginEdit) i zmian nie. możesz następnie zaimplementować właściwość boolean IsDirty, która jest ustawiana, gdy wywoływana jest BeginEdit i czyszczona, gdy wywoływana jest EndEdit lub CancelEdit. (Naprawdę nie rozumiem, dlaczego ta właściwość nie jest częścią IEditableObject; nie zaimplementowałem jeszcze edytowalnego obiektu, który go nie wymagał.)

Oczywiście nie ma potrzeby implementowania tego drugiego poziomu abstrakcji, jeśli go nie potrzebujesz - na pewno możesz wysłuchać PropertyChanged zdarzenia i po prostu założyć, że obiekt został edytowany, jeśli zostanie podniesiony. To naprawdę zależy od twoich wymagań.

 10
Author: Robert Rossney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 21:38:23

Moja Edycja do 'Ta odpowiedź ' jest odrzucona! Więc wrzuciłem tutaj mój edit:

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
}
 4
Author: Behzad Ebrahimi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:47

INotifyCollectionChanged nie jest jednym w tym samym z INotiftyPropertyChanged. Zmiana właściwości bazowych obiektów w żaden sposób nie sugeruje, że kolekcja uległa zmianie.

Jednym ze sposobów osiągnięcia tego zachowania jest utworzenie kolekcji niestandardowej, która będzie przesłuchiwała obiekt po dodaniu i rejestrowała się dla zdarzenia INotifyPropertyChanged.PropertyChanged; wtedy musiałaby się odpowiednio wyrejestrować po usunięciu elementu.

Jedno zastrzeżenie z tym podejściem jest, gdy twoje obiekty są zagnieżdżone N poziomów głęboko. Aby to rozwiązać, musisz zasadniczo przesłuchuje każdą właściwość za pomocą reflection, aby określić, czy jest to być może kolejny zbiór implementujący INotifyCollectionChanged lub inny kontener, który będzie musiał zostać przejechany.

Oto podstawowy, nie przetestowany przykład...

    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)); 
        }
    }
 2
Author: Aaron McIver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 21:59:24

I myślę, że wypełnienie ObservableCollection elementami implementującymi INotifyPropertyChanged spowoduje wywołanie zdarzenia CollectionChanged, gdy element podniesie swoje powiadomienie PropertyChanged.

Po namyśle, myślę, że musisz użyć BindingList<T>, Aby uzyskać indywidualne zmiany przedmiotów, aby propagować w ten sposób po wyjęciu z pudełka.

W przeciwnym razie będziesz musiał ręcznie subskrybować powiadomienia o zmianie każdego elementu i wywołać zdarzenie CollectionChanged. Zauważ, że jeśli tworzysz swój własny, Pochodny ObservableCollection<T>, będziesz musiał subskrybować przy instancjacji i na Add() i Insert(), oraz wypisać się na Remove(), RemoveAt() i Clear(). W przeciwnym razie możesz subskrybować Zdarzenie CollectionChanged i używać dodanych i usuniętych elementów z args zdarzenia do subskrybowania/rezygnacji z subskrypcji.

 1
Author: Jay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 21:15:16

W winforms, {[0] } jest standardową praktyką. w WPF & Silverlight Zwykle utknąłeś w pracy z ObservableCollection i musisz słuchać PropertyChanged na każdym elemencie

 0
Author: Robert Levy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-28 00:56:13

Użyj następującego kodu:

-mój Model:

 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));
        }
    }
}

I w mojej klasie personlist zaimplementować metodę odpowiedzialną za aktywne zwiększanie wartości jednego po kliknięciu przycisku w 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;
    }
}
Mam nadzieję, że komuś się przyda Zauważ, że:
private ObservableCollection<IceCream> Flavors;

-

 0
Author: Sebastian Rycombel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-27 10:38:42

Najprostszym rozwiązaniem, jakie znalazłem, jest usunięcie elementu za pomocą RemoveAt(index), a następnie dodanie zmodyfikowanego elementu do tego samego indeksu za pomocą InsertAt(index) i tym samym ObservableCollection powiadomi Widok.

 0
Author: LightTechnician,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-27 17:45:43