VirtualizingStackPanel + MVVM + множественный выбор
-
16-09-2019 - |
Вопрос
Я реализовал шаблон выбора, подобный тому, который описан в этот пост использование ViewModel для хранения IsSelected значения и путем привязки ListViewItem.IsSelected
к выбранной ViewModel:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListView.ItemContainerStyle>
В целом это работает, но я сталкиваюсь с серьезной проблемой.Используя a VirtualizingStackPanel
как панель в виде списка, только видимый ListViewItem
создаются.Если я использую "Ctrl + A" для выбора всех элементов или с помощью комбинации клавиш, такой как "Shift + Ctrl + End" для первого элемента, все элементы будут выбраны, но для невидимых элементов ViewModel не получит значение IsSelected, равное true.Это логично, потому что если ListViewItem
не созданы, привязка не может работать.
Кто-нибудь сталкивался с такой же проблемой и нашел решение (помимо отказа от использования VirtualizingStackPanel
)?
Решение
Я нашел другой способ обработки выбора в шаблоне MVVM, который решил мою проблему.Вместо сохранения выделения в viewmodel, выделение извлекается из ListView / ListBox и передается в качестве параметра Команде.Все сделано в XAML:
<ListView
x:Name="_items"
ItemsSource="{Binding Items}" ... />
<Button
Content="Remove Selected"
Command="{Binding RemoveSelectedItemsCommand}"
CommandParameter="{Binding ElementName=_items, Path=SelectedItems}"/>
на мой взгляд, модель:
private void RemoveSelection(object parameter)
{
IList selection = (IList)parameter;
...
}
Другие советы
В моем случае я решил эту проблему, создав класс ListBoxEx из ListBox и добавив код для реагирования на изменения выбора, применяя состояние выбора к моделям представления элементов:
private readonly List<IListItemViewModelBase> selectedItems = new List<IListItemViewModelBase>();
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
bool isVirtualizing = VirtualizingStackPanel.GetIsVirtualizing(this);
bool isMultiSelect = (SelectionMode != SelectionMode.Single);
if (isVirtualizing && isMultiSelect)
{
var newSelectedItems = SelectedItems.Cast<IListItemViewModelBase>();
foreach (var deselectedItem in this.selectedItems.Except(newSelectedItems))
{
deselectedItem.IsSelected = false;
}
this.selectedItems.Clear();
this.selectedItems.AddRange(newSelectedItems);
foreach (var newlySelectedItem in this.selectedItems)
{
newlySelectedItem.IsSelected = true;
}
}
}
Помимо того, что вы не используете VirtualizingStackPanel
, единственное, о чем я могу думать, это захватить эти сочетания клавиш и иметь методы для изменения определенного диапазона ваших ViewModel
предметы, чтобы их IsSelected
свойство установлено в True
(например,, SelectAll()
, SelectFromCurrentToEnd()
).В основном в обход Binding
на ListViewItem
для контроля отбора в таких случаях.