2010-08-13 12 views
4

Dans mon viewmodel, j'ai une liste (ObservableCollection) contenant des éléments. Dans la vue, cette liste est affichée dans un ItemsControl. Dans chaque rangée, il y a un bouton "Supprimer". Je veux la commande derrière le bouton pour supprimer l'élément de la liste.WPF: Paramètre de commande pour une commande de suppression dans une liste

<ItemsControl ItemsSource="{Binding myList}"> 
    <ItemsControl.ItemTemplate> 
     ... 
      <Button Command="{StaticResource myDeleteCommand}" CommandParameter="???"> 
       Remove item 
      </Button> 
     ... 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

Que dois-je transmettre en tant que paramètre de commande?

  • L'élément lui-même (Binding .)? Ensuite, je n'ai pas de référence à la liste dans la commande, donc j'ai besoin de changer mon modèle de sorte que chaque élément de liste contienne une référence arrière à la liste.
  • La liste? Ensuite, je n'ai pas de référence à l'article.
  • Les deux? Ensuite, j'ai besoin d'écrire un MultiConverter qui traduit la liste plus l'objet dans un objet personnalisé. On dirait beaucoup de frais généraux pour une tâche aussi simple.

Des idées? Cela me semble un scénario assez commun, donc je suppose qu'il doit y avoir une solution de bonnes pratiques bien établie ...

Répondre

4

J'ai implémenté de telles commandes de cette façon, que je passe l'élément comme paramètre. La commande sait elle-même sur quelle liste elle devrait fonctionner. Soit par un délégué qui appelle une méthode Delete dans mon ViewModel ou la commande reçoit la liste des éléments dans son constructeur.

dire une commande avec les délégués

public sealed class SimpleParameterCommandModel<T> : CommandModel 
{ 
    private readonly Action<T> execute; 
    private readonly Func<T, bool> canExecute; 

    public SimpleParameterCommandModel(string label, string tooltip, Action<T> execute, Func<T, bool> canExecute) 
     : base(appCtx, dataCtx, label, tooltip) 
    { 
     if (execute == null) throw new ArgumentNullException("execute"); 
     this.execute = execute; 
     this.canExecute = canExecute; 
    } 
    ... 
} 
utilisation

:

private ICommand _DeleteCommand = null; 
public ICommand DeleteCommand 
{ 
    get 
    { 
     if (_DeleteCommand == null) 
     { 
      _DeleteCommand = new SimpleParameterCommandModel<IEnumerable<DataObjectModel>>      ("Delete", "Delete selection from data store", 
       (items) => items.ToList().ForEach(i => DeleteItem(i)), 
       (items) => items != null && items.Count() > 0 && AllowDelete); 
     } 
     return _DeleteCommand; 
    } 
} 
public void DeleteItem(DataObjectModel item) 
{ 
     if (item == null) { throw new ArgumentNullException("item"); } 

    myCollection.Remove(item.Object); 
} 

EDIT: Mot de XAML

<Button Command="{Binding DeleteCommand, ElementName=...}" CommandParameter="{Binding}"> 
     Remove item 
</Button> 
+0

Danke! :-) Oui, créer une commande séparée pour chaque instance de la liste est en effet la solution. – Heinzi

3

D'abord, je voudrais gérer la commande dans le ViewModel. Je suppose que la liste qui est utilisée pour la liaison est dans le ViewModel, de sorte que tout code qui "fonctionne" sur cette liste doit également être fait dans le ViewModel.

class MyViewModel 
{ 
    // ... Clipping rest of ViewModel class ... 

    private ObservableCollection<MyObject> mMyList = new ObservableCollection<MyObject>(); 
    private ICommand mMyDeleteCommand; 

    public MyViewModel() 
    { 
     InitializeMyListSomehow(); 
     mMyDeleteCommand = new MyCommandClass(
      (item) => DeleteItem(item), 
      () => mDeleteCanExecute 
     ); 
    } 

    public ObservableCollection<MyObject> MyList 
    { 
     get { return mMyList; } 
     set 
     { 
      // Some function that updates the value and implements INPC 
      SetProperty("MyList", ref mMyList, value); 
     } 
    } 

    public ICommand MyDeleteCommand 
    { 
     get { return mMyDeleteCommand; } 
    } 

    void DeleteHandler(var item) 
    { 
     int index = mMyList.Remove(item); 
    } 


} 

Les éléments sont-ils uniques? Si tel est le cas, vous pouvez passer l'élément et le gestionnaire de commandes Delete peut rechercher l'élément dans la liste.

Si les éléments ne sont pas uniques, vous devrez faire un peu plus de logique, en fonction du résultat attendu.

Maintenant, dans la vue, votre code ressemblera (notez le StaticResource devient une liaison):

<ItemsControl ItemsSource="{Binding MyList}"> 
    <ItemsControl.ItemTemplate> 
     ... 
      <Button Command="{Binding DataContext.MyDeleteCommand, 
           RelativeSource={RelativeSource FindAncestor, 
               AncestorType={x:Type ItemsControl}}}" 
        CommandParameter="{Binding}"> 
       Remove item 
      </Button> 
     ... 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 
+0

Je n'ai pas de référence à 'mMyList' dans le code. Plusieurs listes de ce type sont affichées dans la vue. – Heinzi

+0

Désolé - j'ai manqué que la commande est une source statique. En général, dans une solution MVVM [je crois comprendre que] les commandes sont gérées dans le ViewModel, pas dans la vue. Je vais mettre à jour ma réponse pour refléter cela. –

+0

Merci! Oui, mettre la commande dans le viewmodel de la liste était en effet la voie à suivre. J'ai accepté la solution d'Arthur, car il était un peu plus rapide, mais le vôtre était extrêmement utile aussi! – Heinzi

Questions connexes