VirtualizingStackPanel + MVVM + seleção múltipla
-
16-09-2019 - |
Pergunta
Eu tenho implementado um padrão de seleção semelhante ao descrito no este post usando um ViewModel para armazenar o valor IsSelected, e ligando o ListViewItem.IsSelected
ao ViewModel IsSelected:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListView.ItemContainerStyle>
Ele funciona em geral, mas eu encontrar um problema grave. Usando a um VirtualizingStackPanel
como o painel na vista de lista, apenas o ListViewItem
visíveis estão sendo criados. Se eu usar "Ctrl + A" para selecionar todos os itens, ou usando a combinação de atalho como "Shift + Ctrl + End" no primeiro item, todos os itens são selecionados, mas para os itens não visíveis, o ViewModel não recebe sua IsSelected definido como verdadeiro. Isso é lógico, porque se o ListViewItem
não são criados, a ligação pode não trabalho.
Qualquer pessoa experimentou o mesmo problema, e encontrou uma solução (além de não utilizar um VirtualizingStackPanel
)?
Solução
Eu encontrei outra maneira de lidar com a seleção no padrão MVVM, que resolveu o meu problema. Em vez de manter a seleção na viewmodel, a seleção é recuperado do ListView / ListBox, e passou como um parâmetro para o comando. Tudo feito em XAML:
<ListView
x:Name="_items"
ItemsSource="{Binding Items}" ... />
<Button
Content="Remove Selected"
Command="{Binding RemoveSelectedItemsCommand}"
CommandParameter="{Binding ElementName=_items, Path=SelectedItems}"/>
no meu ViewModel:
private void RemoveSelection(object parameter)
{
IList selection = (IList)parameter;
...
}
Outras dicas
No meu caso, eu acabei resolver este derivando uma classe ListBoxEx de caixa de listagem, e adicionando código para responder a alterações de seleção, impondo o estado de seleção sobre os modelos de exibição artigo:
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;
}
}
}
Além de não usar VirtualizingStackPanel
, a única coisa que posso pensar é capturar esses atalhos de teclado e ter métodos para modificar um determinado intervalo de seus itens ViewModel
para que sua propriedade IsSelected
está definido para True
(por exemplo, SelectAll()
, SelectFromCurrentToEnd()
) . Basicamente ignorando o Binding
em ListViewItem
para controlar a seleção para tais casos.