web-dev-qa-db-fra.com

Gérer plusieurs sélections avec MVVM

Au cours de mon parcours d'apprentissage de MVVM, j'ai acquis une compréhension de base de WPF et du modèle ViewModel. J'utilise l'abstraction suivante pour fournir une liste et je suis intéressé par un seul élément sélectionné.

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 ); } 
}

Je peux ensuite lier OrdersView avec la prise en charge du tri et du filtrage à une liste dans WPF:

<ListView ItemsSource="{Binding Path=OrdersView}" 
          IsSynchronizedWithCurrentItem="True">

Cela fonctionne très bien pour les vues de sélection unique. Mais j'aimerais également prendre en charge plusieurs sélections dans la vue et que le modèle soit lié à la liste des éléments sélectionnés.

Comment lierais-je ListView.SelectedItems à une propriété de backer sur ViewModel?

59
Paul Alexander

Ajoutez une propriété IsSelected à votre ViewModel enfant (OrderViewModel dans votre cas):

public bool IsSelected { get; set; }

Liez la propriété sélectionnée sur le conteneur à ceci (pour ListBox dans ce cas):

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

IsSelected est mis à jour pour correspondre au champ correspondant sur le conteneur.

Vous pouvez obtenir les enfants sélectionnés dans le modèle de vue en procédant comme suit:

public IEnumerable<OrderViewModel> SelectedOrders
{
    get { return Orders.Where(o => o.IsSelected); }
}
94
Josh G

Je peux vous assurer: SelectedItems est en effet contraignable en XAML CommandParameter

Il existe une solution simple à ce problème courant; pour le faire fonctionner, vous devez suivre [~ # ~] tous [~ # ~] les règles suivantes:

  1. Après suggestion d'Ed Ball , sur votre liaison de données de commande XAML, définissez l'attribut CommandParameter [~ # ~] avant [~ # ~] l'attribut Command. C'est un bug qui prend beaucoup de temps.

    enter image description here

  2. Assurez-vous que vos méthodes ICommandCanExecute et Execute ont un paramètre de type object. De cette façon, vous pouvez empêcher les exceptions de cast - silenced qui se produisent chaque fois que le type CommandParameter de la liaison de données ne correspond pas au type de paramètre de votre méthode Command:

    private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)  
    {
         // Your code goes here
    }
    
    private bool OnDeleteSelectedItemsExecute(object SelectedItems)  
    {
        // Your code goes here
    }
    

Par exemple, vous pouvez envoyer la propriété ListView de ListBox/SelectedItems à vos méthodes ICommand ou les ListView/ListBox lui-même. Super, non?

J'espère que cela empêche quelqu'un de passer le temps énorme que j'ai fait pour comprendre comment recevoir SelectedItems en tant que paramètre CanExecute.

12
Julio Nobre

On peut essayer de créer une propriété attachée.

Cela vous évitera d'ajouter la propriété IsSelected pour chaque liste que vous liez. Je l'ai fait pour ListBox, mais il peut être modifié pour être utilisé dans une vue de liste.

<ListBox SelectionMode="Multiple"
         local:ListBoxMultipleSelection.SelectedItems="{Binding SelectedItems}" >

Plus d'informations: WPF - Binding ListBox SelectedItems - Attached Property VS Style .

4
MichaelLo

Si vous utilisez MVVM-LIGHT, vous pouvez utiliser ce modèle:

https://galasoft.ch/posts/2010/05/handling-datagrid-selecteditems-in-an-mvvm-friendly-manner

Pas particulièrement élégant, mais il semble qu'il devrait être fiable au moins

1
Simon_Weaver