Es gibt nicht viele Optionen für ein virtualisierendes Wrap-Panel zur Verwendung in WPF. Aus dem einen oder anderen Grund hat sich MS entschieden, kein Exemplar in die Standardbibliothek zu liefern.
Wenn jemand so mutig sein könnte, dem ersten Workitem des folgenden Codeplex-Projekts eine Crowd Source-Antwort (und Erklärung) zu geben, würde ich das sehr schätzen:
Danke!
Zusammenfassung des Problems:
Ich habe kürzlich versucht, das Wrapper für die Virtualisierung aus diesem Projekt zu verwenden und habe einen Fehler gefunden.
Schritte zum Reproduzieren:
Das Debug.Assert schlägt fehl (Debug.Assert (child == _children [childIndex], "Falsches Kind wurde generiert");) in MeasureOverride, und die weitere Ausführung führt zu einer Null-Ausnahme in der Cleanup-Methode [siehe angehängter Screenshot] .
Bitte lassen Sie mich wissen, wenn Sie dies korrigieren können.
Danke,
AO
Code:
Erläuterung des Problems
Sie haben nach einer Erklärung für das, was schief geht, und nach Anweisungen zur Fehlerbehebung gefragt. Bisher hat niemand das Problem erklärt. Ich werde es tun.
In ListBox mit einem VirtualizingWrapPanel gibt es fünf separate Datenstrukturen, die Elemente auf verschiedene Arten verfolgen:
Wenn ein Element aus ItemsSource entfernt wird, muss diese Entfernung durch alle Datenstrukturen weitergegeben werden. So funktioniert es:
Aus diesem Grund ist die InternalChildren-Sammlung nicht mit den anderen vier Sammlungen synchronisiert, was zu den Fehlern geführt hat, die aufgetreten sind.
Lösung des Problems
Um das Problem zu beheben, fügen Sie den folgenden Code an beliebiger Stelle innerhalb der OnItemsChanged-Methode von VirtualizingWrapPanel hinzu:
%Vor%Dadurch wird die InternalChildren-Sammlung mit den anderen Datenstrukturen synchronisiert.
Warum AddInternalChild / InsertInternalChild hier nicht aufgerufen wird
Sie fragen sich vielleicht, warum im obigen Code keine Aufrufe von InsertInternalChild oder AddInternalChild enthalten sind, und insbesondere, warum wir bei der Verwendung von Ersetzen und Verschieben kein neues Element während OnItemsChanged hinzufügen müssen.
Der Schlüssel zum Verständnis ist die Funktionsweise von ItemContainerGenerator.
Wenn ItemContainerGenerator ein remove-Ereignis empfängt, behandelt es alles sofort:
Auf der anderen Seite lernt ItemContainerGenerator, dass ein Element hinzugefügt wird, alles wird normalerweise zurückgestellt:
Daher müssen alle Entfernungen von der InternalChildren-Sammlung (einschließlich derjenigen, die Teil eines Move oder Replace sind) innerhalb von OnItemsChanged ausgeführt werden, aber Zusätze können (und sollten) bis zum nächsten MeasureOverride zurückgestellt werden.
Beachten Sie zuerst, dass im Allgemeinen, wenn Sie ein Objekt aus einer Sammlung entfernen und Sie es nicht als Referenz haben, dieses Objekt zum Zeitpunkt des Entfernens tot ist. Zumindest RemoveInternalChildRange Aufruf ist nach der Entfernung illegal, aber das ist nicht das Kernproblem.
Zweitens könnte es sein, dass Sie eine kleine Race-Bedingung haben, auch wenn es nicht streng Multi-Threading ist. Überprüfen Sie (mit Haltepunkt), ob dieser Ereignishandler zu eifrig reagiert - Sie möchten nicht, dass der Ereignishandler ausgeführt wird, während Sie sich gerade in der Mitte einer Entfernung befinden, selbst wenn es sich um ein einzelnes Element handelt.
Drittens überprüfen Sie nach:
%Vor%und für den ersten Versuch ändern Sie den Code zu einem graceful exit, was in diesem Fall bedeutet, dass gracefull fortfahren - for-Schleife und Inkremente in der Schleife verwenden müssen, um überhaupt fortfahren zu können.
Überprüfen Sie auch InternalChildren, wenn Sie NULL sehen, um zu sehen, ob dieser Zugriffspfad das gleiche Ergebnis wie Ihre _children liefert (wie Größe, interne Daten, null an der gleichen Stelle).
Wenn nur eine Null übersprungen wird (ohne Ausnahme rendert), dann stoppe sie im Debugger und überprüfe, ob diese Arrays / Collections gesetzt sind (keine Nullen innerhalb).
Veröffentlichen Sie auch das vollständig kompilierbare Beispielprojekt, das die Repro (als Zip-Datei) irgendwo gibt - reduziert zufällige Annahmen und ermöglicht es ppl, nur zu erstellen / auszuführen und zu sehen.
Apropos Annahmen - überprüfen Sie, was Ihre "beobachtbare Sammlung" macht. Wenn Sie ein Objekt aus einer Objektgruppe entfernen, hat jeder Iterator / Enumerator aus einem früheren Zustand dieser Sammlung das Recht, null zu geben oder zu geben und in einer Benutzeroberfläche, die zu schlau zu sein versucht ein veralteter Iterator kann leicht passieren.