Gestione di selezioni multiple con MVVM
-
03-07-2019 - |
Domanda
Nel mio viaggio verso l'apprendimento di MVVM ho stabilito alcune conoscenze di base su WPF e sul modello ViewModel. Sto usando la seguente astrazione quando fornisco un elenco e sono interessato a un singolo elemento selezionato.
public ObservableCollection<OrderViewModel> Orders { get; private set; }
public ICollectionView OrdersView
{
get
{
if( _ordersView == null )
_ordersView = CollectionViewSource.GetDefaultView( Orders );
return _ordersView;
}
}
private ICollectionView _ordersView;
public OrderViewModel CurrentOrder
{
get { return OrdersView.CurrentItem as OrderViewModel; }
set { OrdersView.MoveCurrentTo( value ); }
}
Posso quindi associare il OrdersView insieme a supportare l'ordinamento e il filtro a un elenco in WPF:
<ListView ItemsSource="{Binding Path=OrdersView}"
IsSynchronizedWithCurrentItem="True">
Funziona davvero bene per le viste a selezione singola. Ma vorrei supportare anche selezioni multiple nella vista e fare in modo che il modello si legasse all'elenco degli elementi selezionati.
Come assocerei ListView.SelectedItems a una proprietà backer su ViewModel?
Soluzione
Aggiungi una proprietà IsSelected
a ViewModel di tuo figlio ( OrderViewModel
nel tuo caso):
public bool IsSelected { get; set; }
Associa la proprietà selezionata sul contenitore a questa (per ListBox in questo caso):
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
IsSelected
viene aggiornato per corrispondere al campo corrispondente sul contenitore.
È possibile ottenere i bambini selezionati nel modello di visualizzazione procedendo come segue:
public IEnumerable<OrderViewModel> SelectedOrders
{
get { return Orders.Where(o => o.IsSelected); }
}
Altri suggerimenti
Posso assicurarti che SelectedItems
è effettivamente associabile come XAML CommandParameter
Esiste una soluzione semplice a questo problema comune; per farlo funzionare devi seguire TUTTI le seguenti regole:
-
Seguendo il suggerimento di Ed Ball , nel database dei comandi XAML, definire il
CommandParameter
attributo PRIMA dell'attributoCommand
. Questo è un bug che richiede molto tempo . -
Assicurati che i metodi
ICommand
CanExecute
eExecute
abbiano un parametro di tipooggetto
. In questo modo è possibile impedire le eccezioni silenziate che si verificano ogni volta che il tipoCommandParameter
della banca dati non corrisponde al tipo di parametro del metodoCommand
:private bool OnDeleteSelectedItemsCanExecute(object SelectedItems) { // Your code goes here } private bool OnDeleteSelectedItemsExecute(object SelectedItems) { // Your code goes here }
Ad esempio, puoi inviare una proprietà ListView
/ ListBox
SelectedItems
ai tuoi metodi ICommand
o ListView
/ ListBox
stesso. Fantastico, vero?
Spero che questo impedisca a qualcuno di spendere l'enorme quantità di tempo che ho fatto per capire come ricevere SelectedItems
come parametro CanExecute
.
Si può provare a creare una proprietà collegata.
In questo modo si salverà uno dall'aggiunta della proprietà IsSelected
per ogni elenco associato. L'ho fatto per ListBox
, ma può essere modificato per l'uso in una vista elenco.
<ListBox SelectionMode="Multiple"
local:ListBoxMultipleSelection.SelectedItems="{Binding SelectedItems}" >
Ulteriori informazioni: WPF - ListBox SelectedItems - Proprietà associata VS Style .
Se stai usando MVVM-LIGHT puoi usare questo schema:
Non particolarmente elegante ma sembra che dovrebbe essere affidabile almeno