2016-05-31 12 views
3

Es scheint im erweiterten Auswahlmodus IsSelected Bindung ist fehlerhaft. Sieht so aus, als ob nur das letzte Objekt aus der Auswahl außerhalb des Gültigkeitsbereichs korrekt behandelt wird.Erweiterter Auswahlmodus, Virtualisierung und IsSelected Bindung

Demonstration:

Artikel 0, 1, 2 und 98, 97, 96 mit Steuerung ausgewählt. Bei Auswahl von 94 (ohne Steuerung!) Sollte der Auswahlzähler 1 sein, aber Sie sehen stattdessen 3. Beim Scrollen wird angezeigt, dass nur ein (letzter) Auswahlpunkt außerhalb des Bereichs nicht ausgewählt wurde.

Unten ist MCVE:

XAML:

<ListBox ItemsSource="{Binding Items}" SelectionMode="Extended" SelectionChanged="ListBox_SelectionChanged"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <TextBlock Text="{Binding Text}" /> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
    <ListBox.ItemContainerStyle> 
     <Style TargetType="ListBoxItem"> 
      <Setter Property="IsSelected" Value="{Binding IsSelected}" /> 
     </Style> 
    </ListBox.ItemContainerStyle> 
</ListBox> 

cs:

public class NotifyPropertyChanged : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    public void OnPropertyChanged([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); 
} 

public class Item : NotifyPropertyChanged 
{ 
    bool _isSelected; 
    public bool IsSelected 
    { 
     get { return _isSelected; } 
     set { _isSelected = value; } 
    } 

    public string Text { get; set; } 
} 

public class ViewModel : NotifyPropertyChanged 
{ 
    public ObservableCollection<Item> Items { get; } 

    public ViewModel() 
    { 
     var list = new List<Item>(); 
     for (int i = 0; i < 100; i++) 
      list.Add(new Item() { Text = i.ToString() }); 
     Items = new ObservableCollection<Item>(list); 
    } 
} 

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = new ViewModel(); 
    } 

    void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     Title = ((ViewModel)DataContext).Items.Count(item => item.IsSelected).ToString(); 
    } 
} 

Eine schnelle Lösung ist Listensteuerung (ListBox oder ListView) Virtualisierung deaktivieren:

VirtualizingStackPanel.IsVirtualizing="False" 

Frage: Gibt es eine Idee, wie Sie es beheben können, ohne die Virtualisierung zu deaktivieren?

+0

Ich vermute, die „ausgewählt“ Aspekt der Elemente ist etwas, das Sie brauchen, um sich zu tun, wenn Virtualisierung ist aktiviert. Virtualisierung (zumindest von nativem Windows betroffen) dient lediglich dazu, Hinweise darauf zu geben, was angezeigt werden soll, anstatt es für Sie anzuzeigen. Es funktioniert in Verbindung mit der Bildlaufleiste. Auf diese Weise kann Ihre App 1000 Objekte anzeigen. Die Anzahl der sichtbaren Objekte bleibt im Allgemeinen konstant (außer auf der letzten Seite oder wenn die Anzahl der Elemente geringer ist als normalerweise auf dem Bildschirm angezeigt würde). – MickyD

+0

Es sieht so aus, als wäre der Visualisierungsmodus auf recycle eingestellt, so dass das ausgewählte Objekt wieder verwendet wird. Haben Sie versucht, den Virtualisierungsmodus zu ändern, wie zum Beispiel: 'VirtualizingPanel.VirtualizationMode =" Standard "'? – XAMlMAX

+0

@XAMlMAX, können Sie mcve kopieren und versuchen Sie es selbst, vielleicht finden Sie eine Lösung. 'VirtualizingMode =" Standard "' verbessert nichts (versucht mit 'VirtualizingPanel' und' VirtualizingStackPanel'). – Sinatr

Antwort

3

Nun, das ist das erwartete Verhalten. Die Virtualisierung erstellt nur sichtbare Container (ListBoxItem) für sichtbare Elemente. Damit Bindungen funktionieren, muss der Container an erster Stelle vorhanden sein, sodass nur sichtbare Elemente betroffen sind.

Es gibt zwei offensichtliche Lösungen:

  1. deaktivieren Virtualisierung.
  2. Verwenden Sie stattdessen das Ereignis SelectionChanged. Sie können Objekte hinzufügen und entfernen von SelectionChangedEventArgs. Dann müssen Sie nur noch einen Cast durchführen und die Eigenschaft IsSelected entsprechend setzen (Sie müssen nicht über Items iterieren). Strg + A wird auch funktionieren, einfach zu hinzugefügten Elemente handhaben müssen (und entfernen Sie die Bindung insgesamt):

    void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
        foreach (var removedItem in e.RemovedItems.Cast<Item>()) 
        { 
         removedItem.IsSelected = false; 
        } 
        foreach (var addedItem in e.AddedItems.Cast<Item>()) 
        { 
         addedItem.IsSelected = true; 
        } 
        Title = ((ViewModel) DataContext).Items.Count(item => item.IsSelected).ToString(); 
    } 
    
+0

Wird das letzte Objekt von der Auswahl außerhalb des Bereichs abgewählt, ist auch * erwartetes Verhalten *? Ich konnte es nicht erklären, oder? – Sinatr

+0

@Sinatr, es wird erwartet, dass es nicht ordnungsgemäß funktioniert. Warum es auf diese Weise nicht funktioniert, weiß ich nicht, ich kann nur spekulieren. :) Es hat wahrscheinlich etwas damit zu tun, wie und wann ListViewItems aufgeräumt sind. Wenn ich raten würde, würde ich sagen, dass der zuletzt ausgewählte Objektcontainer irgendwie von der SelectedItem-Eigenschaft "gehalten" wird, daher wird er nicht zerstört, wenn Sie von ihm weg navigieren (im Gegensatz zu allen anderen Objekten in der aktuellen Auswahl). –

+0

Ich verstehe. Ich übersehe Ihren Vorschlag, zuerst hinzugefügte/entfernte Elemente zu verwenden, dies ist nützlich und versucht derzeit, es für 'CTR' +' A' arbeiten zu lassen, ohne 'StackOverflowException' für hinzugefügte Elemente zu erhalten. – Sinatr