Laden...

ObservableCollection: Inhalt austauschen

Erstellt von bb1898 vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.406 Views
B
bb1898 Themenstarter:in
110 Beiträge seit 2008
vor 6 Jahren
ObservableCollection: Inhalt austauschen

Aufgabe: Der Inhalt einer ObservableCollection soll auf Kommando komplett ausgetauscht werden (meist als Ergebnis einer Suche in einem größeren Datenbestand).

Das funktioniert, wenn die neuen Objekte der Liste einzeln zugefügt werden:

public void Refill(IEnumerable<Ship> ships)
{
	Clear();
	foreach (var ship in ships)
	{
		Add(ship);
	}
}

Eigentlich läge es doch aber nahe, die neuen Objekte in einem Rutsch zuzufügen, mit AddRange oder so. Die ObservableCollection hat allerdings keine Methode, die das tut, ich habe in der Dokumentation jedenfalls nichts dergleichen gefunden. Versucht habe ich dies hier:

public class ShipsCollection : ObservableCollection<Ship>
{
	public ShipsCollection() : base() { }

	public ShipsCollection(IEnumerable<Ship> ships) => Refill(ships);

	public void Refill(IEnumerable<Ship> ships)
	{
		Clear();
		((List)Items).AddRange(ships);
	}
}

Das wird zwar anstandslos kompiliert, das Programm startet auch und das Ergebnis der ersten Suche wird richtig angezeigt. So bald aber eine neue Suche ein anderes Ergebnis hat, gibt es eine Exception. Weil der interessante Teil des Textes dazu recht lang ist, stelle ich erst mal meine Frage und hänge ihn danach an:

Stimmt meine Vermutung, dass so ein Komplett-Austausch der Elemente einer ObservableCollection nicht möglich ist oder habe ich nur den richtigen Weg nicht gefunden?

Klar ist, dass ich eine neue Instanz der Collection mit neuem Inhalt erzeugen könnte, aber dann müsste ich INotifyPropertyChanged auf die Collection als Ganzes anwenden. Und wäre das überhaupt eine gute Idee?

Und hier der gekürzte Text zur Exception:

Fehlermeldung:
System.InvalidOperationException ist aufgetreten.
HResult=0x80131509
Nachricht = Ein ItemsControl ist nicht konsistent mit seiner Elementquelle.
Weitere Informationen finden Sie in der inneren Ausnahme.
Quelle = <Die Ausnahmequelle kann nicht ausgewertet werden.>
...
Die Ausnahme wurde ausgelöst, da der Generator für Steuerelement 'System.Windows.Controls.DataGrid Items.Count:5' mit dem Namen '(unbenannt)' eine Reihe von CollectionChanged-Ereignissen empfangen hat, die nicht mit dem aktuellen Status der Elementsammlung übereinstimmen. Die folgenden Unterschiede wurden festgestellt:
Gesammelte Anzahl 1 unterscheidet sich von der tatsächlichen Anzahl 5. [Gesammelte Anzahl ist (Anzahl bei letztem Reset + #Adds - #Removes seit letztem Reset).]

Eine oder mehrere der folgenden Quellen haben möglicherweise falsche Ereignisse ausgelöst:
System.Windows.Controls.ItemContainerGenerator
System.Windows.Controls.ItemCollection
System.Windows.Data.ListCollectionView

  • ShipsCrud.ShipsCollection  
    

(Die beteiligten Quellen werden als die wahrscheinlichere Ursache des Problems betrachtet.)

Die häufigsten Ursachen umfassen (a) das Ändern der Sammlung oder deren Anzahl ohne Auslösen eines entsprechenden Ereignisses sowie (b) das Auslösen eines Ereignisses mit falschem Index- oder Elementparameter.

T
2.219 Beiträge seit 2008
vor 6 Jahren

@bb1898
Wenn du nur auf Standard Funktionen der ObservableCollection Collection zugreifst, dann brauchst du keine Ableitung.
Am besten wäre es, wenn du deine "ShipsCollection " nur als Container implementierst.
Dann hast du eine interne ObservableCollection vom Typ Ship und kannst dann darauf arbeiten.
Eine Ableitung von einer Kollektion wäre hier nicht sinnvoll, da du diese nicht neu implementieren willst sonstn nur die entsprechenden Methoden wiederverwendest.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

D
985 Beiträge seit 2014
vor 6 Jahren

Der einfachste und schnellste Weg ist das Erstellen einer neuen ObservableCollection.

3.170 Beiträge seit 2006
vor 6 Jahren

Hallo,

wenn Du ohnehin den kompletten Inhalt austauschst, könntest Du Dir auch überlegen, die ObservableCollection selbst auszutauschen (also eine ganz neue Collection zu erzeugen).

Gruß, MarsStein

Edit:
öhmm zu spät. Aber ich poste es trotzdem, um die Ansicht von Sir Rufo zu unterstützen.

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

4.931 Beiträge seit 2008
vor 6 Jahren

Hallo,

bzgl. der Exception. Dies liegt daran, daß bei der ObservableCollection<T> die virtuelle Methode InsertItem überschrieben ist:


protected override void InsertItem(int index, T item)
{
    this.CheckReentrancy();
    base.InsertItem(index, item);
    this.OnPropertyChanged("Count");
    this.OnPropertyChanged("Item[]");
    this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}

Und daher führt ((List)Items).AddRange(ships) keine PropertyChanged/CollectionChanged-Ereignisse aus, welches wiederum für die Konsistenz nötig sind.

B
bb1898 Themenstarter:in
110 Beiträge seit 2008
vor 6 Jahren

Das ging ja fix! Danke an alle Antwortenden.

bzgl. der Exception. Dies liegt daran, daß bei der ObservableCollection<T> die virtuelle Methode InsertItem überschrieben ist

Und daher führt ((List)Items).AddRange(ships) keine PropertyChanged/CollectionChanged-Ereignisse aus, welches wiederum für die Konsistenz nötig sind.

Danke für die Klärung! Dann habe ich also die Wahl zwischen einer Schleife mit Add-Aufrufen und einer neuen Instanz. Na schön.

@T-Virus: Ich habe die eigene Subklasse ShipsCollection nur erstellt, um an die Items-Eigenschaft heranzukommen, die ist ja protected. Aber nachdem das nicht tut, was ich mir davon erhofft habe, ist die Klasse nicht mehr nötig.

L
155 Beiträge seit 2012
vor 6 Jahren

Oder du benutzt eine BulkObservableCollection<T> in dem du die BulkCollection<T> von Visual Studio nimmst oder du baust sie dir selbst.

Mit freundlichen Grüßen
lutzeslife