Implementieren von CollectionChanged

Ich habe CollectionChanged eventhandler(onCollectionChanged) zu einer ObservableCollection Eigenschaft hinzugefügt.

Ich habe herausgefunden, dass die onCollectionChanged Methode nur aufgerufen wird, wenn Elemente hinzugefügt oder Elemente zur Sammlung entfernt werden, aber nicht, wenn das Sammlungselement bearbeitet wird.

Ich würde gerne wissen, wie man die Liste / Sammlung neu hinzugefügter, entfernter und bearbeiteter Objekte in einer einzigen Sammlung sendet.

Vielen Dank.

Sie müssen jedem Element einen PropertyChanged Listener hinzufügen (der INotifyPropertyChanged implementieren INotifyPropertyChanged ), um Benachrichtigungen zum Bearbeiten von Objekten in einer beobachtbaren Liste zu erhalten.

 public ObservableCollection Names { get; set; } public List ModifiedItems { get; set; } public ViewModel() { this.ModifiedItems = new List(); this.Names = new ObservableCollection(); 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); } 

Vielleicht müssen Sie prüfen, ob sich ein Element bereits in der ModifedItems-Liste befindet (mit Lists Methode Contains (object obj)) und nur ein neues Element hinzufügen, wenn das Ergebnis dieser Methode false ist .

Die class Item muss INotifyPropertyChanged implementieren. Sehen Sie dieses Beispiel, um zu wissen, wie. Wie Robert Rossney sagte, können Sie das auch mit IEditableObject – wenn Sie diese Anforderung haben.

Ein ItemsControl CollectionChanged , um die Anzeige der Sammlung von Elementen zu verwalten, die auf dem Bildschirm angezeigt werden. Ein ContentControl PropertyChanged , um die Anzeige des bestimmten Elements zu verwalten, das auf dem Bildschirm angezeigt wird. Es ist ziemlich einfach, die beiden Konzepte in Ihrem Kopf zu trennen, sobald Sie das verstanden haben.

Die Verfolgung, ob ein Element bearbeitet wird oder nicht, ist keine dieser Schnittstellen. Eigenschaftsänderungen werden nicht bearbeitet, dh sie stellen nicht unbedingt eine vom Benutzer initiierte Änderung des Objektstatus dar. Zum Beispiel könnte ein Objekt eine ElapsedTime Eigenschaft haben, die ständig von einem Timer aktualisiert wird; Die Benutzerschnittstelle muss über diese Eigenschaftsänderungsereignisse benachrichtigt werden, aber sie stellen sicherlich keine Änderungen an den zugrunde liegenden Daten des Objekts dar.

Die Standardmethode zum Verfolgen, ob ein Objekt bearbeitet wird, besteht darin, dieses Objekt zuerst mit IEditableObject implementieren. Sie können dann intern in der class des Objekts entscheiden, welche Änderungen eine Bearbeitung darstellen (dh Sie müssen BeginEdit ) und welche Änderungen nicht. Sie können dann eine boolesche IsDirty Eigenschaft implementieren, die beim BeginEdit und gelöscht wird, wenn EndEdit oder CancelEdit aufgerufen wird. (Ich verstehe wirklich nicht, warum diese Eigenschaft nicht Teil von IEditableObject ; ich habe noch kein bearbeitbares Objekt implementiert, das es nicht benötigt.)

Natürlich ist es nicht notwendig, diese zweite Abstraktionsebene zu implementieren, wenn Sie sie nicht brauchen – Sie können das PropertyChanged Ereignis mit Sicherheit anhören und davon ausgehen, dass das Objekt bearbeitet wurde, wenn es ausgetriggers wird. Es hängt wirklich von Ihren Anforderungen ab.

Meine Bearbeitung zu ” dieser Antwort ” wird abgelehnt! Also habe ich meine Bearbeitung hier eingefügt:

 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 } 

INotifyCollectionChanged ist nicht identisch mit INotiftyPropertyChanged . Das Ändern der Eigenschaften von zugrunde liegenden Objekten deutet in keiner Weise darauf hin, dass sich die Sammlung geändert hat.

Eine Möglichkeit, um dieses Verhalten zu erreichen, besteht darin, eine benutzerdefinierte Sammlung zu erstellen, die das einmal hinzugefügte Objekt INotifyPropertyChanged.PropertyChanged und für das INotifyPropertyChanged.PropertyChanged Ereignis registriert. es müsste sich dann entsprechend abmelden, wenn ein Gegenstand entfernt wird.

Ein Nachteil bei diesem Ansatz ist, wenn Ihre Objekte N Ebenen tief geschachtelt sind. Um dies zu lösen, müssen Sie jede Eigenschaft im Wesentlichen mithilfe von Reflektion abfragen, um festzustellen, ob es sich vielleicht noch um eine weitere Sammlung handelt, die INotifyCollectionChanged oder einen anderen Container implementiert, der durchlaufen werden muss.

Hier ist ein rudimentäres, nicht getestetes Beispiel …

  public class ObservableCollectionExt : ObservableCollection { 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)); } } 

Ich denke , das INotifyPropertyChanged der ObservableCollection mit Elementen, die INotifyPropertyChanged implementieren, INotifyPropertyChanged dazu, dass das CollectionChanged Ereignis INotifyPropertyChanged wird, wenn ein Element seine PropertyChanged Benachrichtigung INotifyPropertyChanged .

Auf den zweiten Gedanken, denke ich, müssen Sie BindingList , um einzelne BindingList zu erhalten, die auf diese Weise out-of-the-box verbreitet werden.

Andernfalls müssen Sie die Änderungsbenachrichtigungen für jedes Element manuell abonnieren und das CollectionChanged Ereignis auslösen. Beachten Sie, dass Sie bei der Erstellung Ihrer eigenen abgeleiteten ObservableCollection bei der Instantiierung und bei Add() und Insert() RemoveAt() und sich bei Remove() , RemoveAt() und Clear() abmelden müssen. Andernfalls können Sie das CollectionChanged Ereignis abonnieren und die hinzugefügten und entfernten Elemente aus den Ereignisargumenten zum Abonnieren / Abbestellen verwenden.

In Winforms ist BindingList Standard. In WPF & Silverlight arbeiten Sie normalerweise mit ObservableCollection und müssen für jedes Element nach PropertyChanged suchen

Verwenden Sie den folgenden Code:

-mein Modell:

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

Und in meiner class implementiere PersonList Methode, um den Wert eines nach dem Klick in AppBarControl aktiv zu erhöhen

 async private void Add_Click(object sender, RoutedEventArgs e) { List items = new List(); 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; } } 

Ich hoffe, dass es für jemanden nützlich sein wird zu bemerken, dass:

 private ObservableCollection Flavors; 

Die einfachste Lösung, die ich für diese Einschränkung gefunden habe, ist, das Element mit RemoveAt(index) zu entfernen und dann das geänderte Element mit InsertAt(index) auf demselben Index InsertAt(index) sodass die ObservableCollection die Ansicht benachrichtigen wird.