In meinem Programm habe ich eine Reihe von View-Model-Objekten, um Objekte in einer ListBox darzustellen (Mehrfachauswahl ist erlaubt). Das Viewmodel verfügt über eine IsSelected-Eigenschaft, die ich an die ListBox binden möchte, damit der Auswahlstatus im Viewmodel und nicht in der Listbox selbst verwaltet wird.
Offensichtlich verwaltet die ListBox jedoch keine Bindungen für die meisten Offscreen-Elemente, sodass die IsSelected-Eigenschaft im Allgemeinen nicht korrekt synchronisiert wird. Hier ist ein Code, der das Problem veranschaulicht. Erstes XAML:
%Vor%C # Wählen Sie Alle Handler:
%Vor%C # Viewmodel:
%Vor%Zwei verschiedene Probleme können beobachtet werden.
Meine Fragen sind:
ListBox
wird standardmäßig UI virtualisiert. Das bedeutet, dass zu einem bestimmten Zeitpunkt nur die sichtbaren Elemente (zusammen mit einer kleinen Untergruppe von "fast sichtbaren" Elementen) in ItemsSource
tatsächlich gerendert werden. Das erklärt, warum das Aktualisieren der source erwartungsgemäß funktioniert (da diese Elemente immer vorhanden sind), aber nur das Navigieren auf der Benutzeroberfläche nicht (da die visuellen Darstellungen dieser Elemente im laufenden Betrieb erstellt und zerstört werden und niemals gleichzeitig existieren.)
Wenn Sie dieses Verhalten deaktivieren möchten, können Sie ScrollViewer.CanContentScroll=False
auf Ihrem ListBox
festlegen. Dies ermöglicht ein "sanftes" Scrollen und schaltet die Virtualisierung implizit aus. Um die Virtualisierung explizit zu deaktivieren, können Sie VirtualizingStackPanel.IsVirtualizing=False
festlegen.
Das Ausschalten der Virtualisierung ist oft nicht möglich. Wie die Leute bemerkt haben, ist die Leistung mit vielen Gegenständen schrecklich.
Der Hack, der für mich zu funktionieren scheint, ist das Anhängen eines StatusChanged-Listeners an den ItemContainerGenerator der Listbox. Wenn neue Elemente in die Ansicht gerollt werden, wird der Listener aufgerufen, und Sie können die Bindung festlegen, wenn sie nicht vorhanden ist.
In der Datei Example.xaml.cs:
%Vor% Es gibt einen Weg um dies zu vermeiden, der keine Deaktivierung der Virtualisierung erfordert (was die Performance beeinträchtigt). Das Problem (wie in der vorherigen Antwort erwähnt) ist, dass Sie sich nicht auf einen ItemContainerStyle verlassen können, um IsSelected
auf allen Ihren Viewmodels zuverlässig zu aktualisieren, da die Itemcontainer nur für sichtbare Elemente existieren. Sie können jedoch alle ausgewählten Elemente aus der Eigenschaft SelectedItems
der ListBox abrufen.
Dies erfordert die Kommunikation vom View-Modell zur View, was normalerweise ein Nein-Nein für die Verletzung der MVVM-Prinzipien ist. Aber es gibt ein Muster, mit dem alles funktioniert und Ihre ViewModel-Einheit testbar bleibt. Erstellen Sie eine Ansichtsschnittstelle für die VM, mit der gesprochen werden soll:
%Vor%Fügen Sie in Ihrem Ansichtsmodell eine View-Eigenschaft hinzu:
%Vor%Abonnieren Sie in Ihrer Ansicht OnDataContextChanged und führen Sie Folgendes aus:
%Vor%Und implementieren Sie auch die SelectedItems-Eigenschaft:
%Vor% Dann können Sie in Ihrem ViewModel alle ausgewählten Objekte nach this.View.SelectedItems
.
Wenn Sie Komponententests schreiben, können Sie festlegen, dass IMainView das tut, was Sie wollen.