Lancer un événement de double clic à partir d’un élément WPF ListView à l’aide de MVVM

Dans une application WPF utilisant MVVM, j’ai un contrôle utilisateur avec un élément de vue liste. Lors de l’exécution, il utilisera la liaison de données pour remplir la liste avec une collection d’objects.

Quelle est la manière correcte d’attacher un événement de double-clic aux éléments de la vue de liste afin qu’un élément de la vue de liste soit double, un événement correspondant dans le modèle de vue soit déclenché et une référence à l’élément cliqué?

Comment peut-on le faire de manière MVVM propre, c’est-à-dire sans code derrière dans la vue?

S’il vous plaît, le code derrière n’est pas une mauvaise chose du tout. Malheureusement, beaucoup de gens dans la communauté WPF se sont trompés.

MVVM n’est pas un modèle pour éliminer le code derrière. C’est séparer la partie vue (apparence, animations, etc.) de la partie logique (workflow). De plus, vous pouvez tester le composant logique.

Je connais suffisamment de scénarios dans lesquels vous devez écrire du code parce que la liaison de données n’est pas une solution à tout. Dans votre scénario, je traiterais l’événement DoubleClick dans le code derrière le fichier et déléguer cet appel au ViewModel.

Vous trouverez des exemples d’applications utilisant du code derrière et respectant toujours la séparation MVVM:

WPF Application Framework (WAF) – http://waf.codeplex.com

Je suis capable de faire fonctionner ceci avec .NET 4.5. Semble simple et aucun tiers ou code derrière nécessaire.

                      

J’aime utiliser les comportements et commandes de commande attachés . Marlon Grech a une très bonne implémentation des comportements de commande attachés. À l’aide de ceux-ci, nous pourrions ensuite atsortingbuer un style à la propriété ItemContainerStyle de ListView qui définira la commande pour chaque ListViewItem.

Ici, nous définissons la commande à déclencher sur l’événement MouseDoubleClick, et le paramètre CommandParameter sera l’object de données sur lequel nous cliquons. Ici, je parcours l’arborescence visuelle pour obtenir la commande que j’utilise, mais vous pouvez tout aussi bien créer des commandes à l’échelle de l’application.

  

Pour les commandes, vous pouvez soit implémenter directement une commande ICommand , soit utiliser certains des assistants tels que ceux fournis dans MVVM Toolkit .

J’ai trouvé un moyen très simple et facile de le faire avec les déclencheurs d’événement du SDK Blend. Nettoyer MVVM, réutilisable et sans code-behind.

Vous avez probablement déjà quelque chose comme ça:

  

Maintenant, incluez un ControlTemplate pour ListViewItem comme ceci si vous n’en utilisez pas déjà un:

        

GridViewRowPresenter sera la racine visuelle de tous les éléments “internes” constituant un élément de ligne de liste. Maintenant, nous pourrions insérer un déclencheur pour rechercher les événements routés MouseDoubleClick et appeler une commande via InvokeCommandAction comme ceci:

              

Si vous avez des éléments visuels “au-dessus” de GridRowPresenter (en commençant par une grid), vous pouvez également y placer le déclencheur.

Malheureusement, les événements MouseDoubleClick ne sont pas générés à partir de chaque élément visuel (ils proviennent des contrôles, mais pas des FrameworkElements par exemple). Une solution consiste à dériver une classe à partir de EventTrigger et à rechercher MouseButtonEventArgs avec un ClickCount de 2. Cela filtre efficacement tous les non-MouseButtonEvents et tous les MoseButtonEvents avec un ClickCount! = 2.

 class DoubleClickEventTrigger : EventTrigger { protected override void OnEvent(EventArgs eventArgs) { var e = eventArgs as MouseButtonEventArgs; if (e == null) { return; } if (e.ClickCount == 2) { base.OnEvent(eventArgs); } } } 

Maintenant, nous pouvons écrire ceci (‘h’ est l’espace de noms de la classe d’assistance ci-dessus):

              

Je me rends compte que cette discussion a un an, mais avec .NET 4, y a-t-il des idées sur cette solution? Je suis tout à fait d’accord pour dire que le but de MVVM n’est PAS d’éliminer un code derrière un fichier. Je suis également très convaincu que le fait que quelque chose soit compliqué ne signifie pas que c’est mieux. Voici ce que j’ai mis dans le code derrière:

  private void ButtonClick(object sender, RoutedEventArgs e) { dynamic viewModel = DataContext; viewModel.ButtonClick(sender, e); } 

Vous pouvez utiliser la fonction Action de Caliburn pour mapper des événements sur des méthodes de votre ViewModel. En supposant que vous avez une méthode ItemActivated sur votre ViewModel , alors XAML correspondant ressemblerait à ViewModel :

  

Pour plus de détails, vous pouvez examiner la documentation et les échantillons de Caliburn.

Je trouve plus simple de lier la commande lorsque la vue est créée:

 var r = new MyView(); r.MouseDoubleClick += (s, ev) => ViewModel.MyCommand.Execute(null); BindAndShow(r, ViewModel); 

Dans mon cas, BindAndShow ressemble à ceci (updatecontrols + avalondock):

 private void BindAndShow(DockableContent view, object viewModel) { view.DataContext = ForView.Wrap(viewModel); view.ShowAsDocument(dockManager); view.Focus(); } 

Bien que l’approche devrait fonctionner avec la méthode que vous avez pour ouvrir de nouvelles vues.

J’ai vu la solution de rushui avec les InuptBindings mais je n’arrivais toujours pas à atteindre la zone du ListViewItem où il n’y avait pas de texte – même après avoir défini l’arrière-plan comme transparent, je l’ai résolu en utilisant différents modèles.

Ce modèle est destiné à la sélection de ListViewItem et est actif:

          

Ce modèle est utilisé lorsque le ListViewItem a été sélectionné et est inactif:

      

Ceci est le style par défaut utilisé pour ListViewItem:

  

Ce que je n’aime pas, c’est la répétition du TextBlock et de sa reliure, je ne sais pas si je peux le dire en un seul endroit.

J’espère que ça aidera quelqu’un!