2016-05-31 3 views
3

Кажется, в режиме расширенного выбора IsSelected привязка является ошибкой. Похоже, что только последний элемент от выбора, который становится из области обрабатывается должным образом.Режим расширенного выбора, виртуализация и привязка IsSelected

Демонстрация:

товары 0, 1, 2 и 98, 97, 96 выбираются Control. При выборе 94 (без Управление!) Счетчик посещений должен быть 1, но вместо этого вы видите 3. Прокрутка показывает, что только один (последний) элемент выделения из области не был выбран.

Ниже 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(); 
    } 
} 

Быстрое исправление отключить контроль списка (ListBox или ListView) виртуализацию:

VirtualizingStackPanel.IsVirtualizing="False" 

Вопрос: любая идея, как исправить это, не отключая виртуализацию?

+0

Я подозреваю, что «выбран» аспект пунктов является то, что вам нужно сделать самостоятельно, когда виртуализация включена. Виртуализация (по крайней мере, из родной Windows) заключается только в том, чтобы дать подсказки относительно того, какие элементы должны отображаться, а не отображать их для вас. Он работает в сочетании с полосой прокрутки. Это позволяет вашему приложению показывать 1000 предметов. Количество видимых элементов обычно остается неизменным (за исключением последней страницы или количества элементов меньше, чем обычно на экране). – MickyD

+0

Мне кажется, что режим визуализации установлен на переработку, поэтому он будет повторно использовать выбранный элемент. Пробовали ли вы изменить режим виртуализации, например: 'VirtualizingPanel.VirtualizationMode =« Standard »'? – XAMlMAX

+0

@XAMlMAX, вы можете скопировать mcve и попробовать сами, может быть, вы найдете решение. 'VirtualizingMode =" Standard "' ничего не улучшает (используется с 'VirtualizingPanel' и' VirtualizingStackPanel'). – Sinatr

ответ

3

Ну, это ожидаемое поведение. Виртуализация создает только визуальные контейнеры (ListBoxItem) для видимых элементов. Для того, чтобы привязки работали, контейнер должен существовать в первую очередь, поэтому затрагиваются только видимые элементы.

Есть два очевидных решения:

  1. Отключить виртуализации.
  2. Используйте вместо этого SelectionChanged. Вы можете добавлять и удалять элементы из SelectionChangedEventArgs. Затем все, что вам нужно сделать, это выполнить бросок и установить свойство IsSelected соответственно (вам не нужно перебирать Items). Ctrl + A будет работать так же хорошо, вы просто должны обрабатывать добавленные элементы тоже (и удалить связывание в целом):

    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

Отменить выбор последнего элемента из-за пределов области видимости также * ожидаемое поведение *? Я не мог объяснить это, не так ли? – Sinatr

+0

@Sinatr, ожидается, что он не будет работать должным образом. Почему это не работает именно таким образом, я не знаю, я могу только догадываться. :) Вероятно, это связано с тем, как и когда ListViewItems очищаются. Если бы я мог догадаться, я бы сказал, что последний выбранный контейнер элементов каким-то образом «удерживается» по свойству 'SelectedItem', поэтому он не уничтожается, когда вы перемещаетесь от него (в отличие от всех остальных элементов текущего выбора). –

+0

Вижу. Я упускаю из виду ваше предложение использовать добавленные/удаленные элементы сначала, это полезно, в настоящее время пытается заставить его работать для 'CTR' +' A', не получая 'StackOverflowException' для добавленных элементов. – Sinatr