WPF: Die Bindung an ListBoxItem.IsSelected funktioniert nicht für Objekte außerhalb des Bildschirms

8

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.

  1. Wenn Sie auf das erste Element klicken und dann Umschalt + Ende drücken, sollten alle 200 Elemente ausgewählt werden. Die Überschrift meldet jedoch, dass nur 21 Elemente ausgewählt sind.
  2. Wenn Sie auf "Alle auswählen" klicken, werden alle Elemente tatsächlich ausgewählt. Wenn Sie dann auf ein Element in der ListBox klicken, würden Sie erwarten, dass die anderen 199 Elemente deaktiviert werden. Dies geschieht jedoch nicht. Stattdessen werden nur die Elemente auf dem Bildschirm (und einige andere) deaktiviert. Alle 199 Elemente werden nicht abgewählt, es sei denn, Sie scrollen zuerst durch die Liste von Anfang bis Ende (und selbst dann, seltsam genug, funktioniert es nicht, wenn Sie mit dem kleinen Bildlauffeld scrollen).

Meine Fragen sind:

  1. Kann jemand genau erklären, warum dies geschieht?
  2. Kann ich das Problem umgehen oder umgehen?
Qwertie 17.08.2011, 18:58
quelle

3 Antworten

11

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.

    
dlev 17.08.2011, 19:05
quelle
2

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%     
ryanman 12.06.2014 21:21
quelle
1

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 .

holen

Wenn Sie Komponententests schreiben, können Sie festlegen, dass IMainView das tut, was Sie wollen.

    
RandomEngy 03.02.2018 17:47
quelle

Tags und Links