VirtualizingStackPanel + MVVM + множественный выбор

Я реализовал шаблон выбора, аналогичный описанному в этом посте, используя ViewModel для хранения значения IsSelected и связывая ListViewItem.IsSelected выбранной ViewModel:

<ListView.ItemContainerStyle>
    <Style TargetType="{x:Type ListViewItem}">
        <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
    </Style>
</ListView.ItemContainerStyle>

В целом это работает, но я сталкиваюсь с серьезной проблемой. Используя VirtualizingStackPanel в качестве панели в виде списка, только видимые ListViewItem создаются Если я использую "Ctrl+A" для выбора всех элементов или с помощью сочетания клавиш, такого как "Shift+Ctrl+End", для первого элемента, все элементы выбираются, но для невидимых элементов ViewModel не получает свой IsSelected установите в true. Это логично, потому что если ListViewItem не создаются, привязка не может работать.

Кто-нибудь сталкивался с такой же проблемой и нашел решение (кроме того, что не использовал VirtualizingStackPanel)?

3 ответа

Решение

Я нашел другой способ обработки выделения в шаблоне MVVM, который решил мою проблему. Вместо того, чтобы поддерживать выбор в модели представления, выборка извлекается из ListView/ListBox и передается в качестве параметра в Команду. Все сделано в XAML:

<ListView 
    x:Name="_items"
    ItemsSource="{Binding Items}" ... />

<Button 
    Content="Remove Selected" 
    Command="{Binding RemoveSelectedItemsCommand}" 
    CommandParameter="{Binding ElementName=_items, Path=SelectedItems}"/>

в моей ViewModel:

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 для контроля выбора для таких случаев.

Другие вопросы по тегам