Je travaille actuellement avec le modèle Microsoft MVVM et je trouve frustrant le manque d’exemples détaillés. L'exemple ContactBook fourni montre très peu de gestion de commande et le seul autre exemple que j'ai trouvé provient d'un article de MSDN Magazine dans lequel les concepts sont similaires mais utilisent une approche légèrement différente et manquent encore de complexité. Existe-t-il des exemples MVVM décents montrant au moins les opérations CRUD de base et le changement de dialogue/contenu?
Les suggestions de chacun ont été vraiment utiles et je vais commencer à dresser une liste de bonnes ressources
Cadres/Modèles
Articles utiles
Screencasts
Bibliothèques supplémentaires
Malheureusement, il n'y a pas une seule application exemple MVVM qui fait tout, et il y a beaucoup d'approches différentes pour faire les choses. Tout d’abord, vous voudrez peut-être vous familiariser avec l’un des frameworks d’application existants (Prism est un bon choix), car ils vous fournissent des outils pratiques comme l’injection de dépendances, les commandes, l’agrégation d’événements, etc. pour facilement essayer différents modèles qui vous conviennent. .
La libération du prisme:
http://www.codeplex.com/CompositeWPF
Il comprend un exemple d'application assez décent (le négociant en actions) ainsi que de nombreux exemples plus petits et comment. À tout le moins, c'est une bonne démonstration de plusieurs sous-modèles courants utilisés par les utilisateurs pour faire fonctionner MVVM. Ils ont des exemples pour CRUD et les dialogues, je crois.
Prism n'est pas nécessairement pour tous les projets, mais c'est une bonne chose à connaître.
CRUD: Cette partie est assez simple, les liaisons bidirectionnelles WPF facilitent la modification de la plupart des données. Le vrai truc est de fournir un modèle qui facilite la configuration de l'interface utilisateur. À tout le moins, vous voulez vous assurer que votre ViewModel (ou objet métier) implémente INotifyPropertyChanged
pour prendre en charge la liaison et que vous pouvez lier des propriétés directement aux contrôles de l'interface utilisateur, mais vous pouvez également implémenter IDataErrorInfo
. pour validation. En général, si vous utilisez une sorte de solution ORM, configurer CRUD est un jeu d'enfant.
Cet article illustre des opérations simples crud: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx
Il est construit sur LinqToSql, mais cela n’est pas pertinent pour cet exemple. Tout ce qui est important, c’est que vos objets métier implémentent INotifyPropertyChanged
(ce que font les classes générées par LinqToSql). MVVM n'est pas le but de cet exemple, mais je ne pense pas que cela compte dans ce cas.
Cet article montre la validation des données
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx
Encore une fois, la plupart des solutions ORM génèrent des classes qui implémentent déjà IDataErrorInfo
et fournissent généralement un mécanisme facilitant l’ajout de règles de validation personnalisées.
La plupart du temps, vous pouvez prendre un objet (modèle) créé par un ORM et l'envelopper dans un ViewModel contenant les commandes à enregistrer/supprimer - et vous êtes prêt à lier l'interface utilisateur directement aux propriétés du modèle.
La vue ressemblerait à quelque chose comme ceci (ViewModel a une propriété Item
qui contient le modèle, comme une classe créée dans l'ORM):
<StackPanel>
<StackPanel DataContext=Item>
<TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
</StackPanel>
<Button Command="{Binding SaveCommand}" />
<Button Command="{Binding CancelCommand}" />
</StackPanel>
Dialogues: Les dialogues et MVVM sont un peu délicats. Je préfère utiliser une variante de l'approche Mediator avec les dialogues, vous pouvez en lire un peu plus à ce sujet dans cette question de StackOverflow:
Exemple de dialogue WPF MVVM
Mon approche habituelle, qui n’est pas un MVVM classique, peut être résumée comme suit:
Une classe de base pour un dialogue ViewModel qui expose les commandes pour les actions de validation et d'annulation, un événement pour indiquer à la vue qu'un dialogue est prêt à être fermé et tout ce dont vous aurez besoin dans tous vos dialogues.
Une vue générique pour votre boîte de dialogue - il peut s’agir d’une fenêtre ou d’un contrôle de type "modal" personnalisé. À la base, il s'agit d'un présentateur de contenu dans lequel nous vidons le modèle de vue. Ce dernier gère le câblage nécessaire à la fermeture de la fenêtre. Par exemple, lors d'un changement de contexte de données, vous pouvez vérifier si le nouveau ViewModel est hérité de votre classe de base et, le cas échéant, s'abonner à l'événement de fermeture approprié (le gestionnaire affectera le résultat de la boîte de dialogue). Si vous fournissez une autre fonctionnalité de fermeture universelle (le bouton X, par exemple), vous devez également veiller à exécuter la commande de fermeture correspondante sur le ViewModel.
Quelque part, vous devez fournir des modèles de données pour vos modèles de vue, ils peuvent être très simples, d'autant plus que vous avez probablement une vue pour chaque boîte de dialogue encapsulée dans un contrôle séparé. Le modèle de données par défaut pour un ViewModel ressemblerait alors à ceci:
<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
<views:AddressEditView DataContext="{Binding}" />
</DataTemplate>
La vue de dialogue doit avoir accès à ceux-ci, car sinon, elle ne saura pas montrer le ViewModel, mis à part l'interface utilisateur de la boîte de dialogue partagée, son contenu est essentiellement le suivant:
<ContentControl Content="{Binding}" />
Le modèle de données implicite mappera la vue sur le modèle, mais qui la lance?
Ceci est la partie pas-si-mvvm. Une façon de le faire est d'utiliser un événement global. Ce que je pense être une meilleure chose à faire est d'utiliser une configuration de type agrégateur d'événements, fournie par injection de dépendance - ainsi, l'événement est global pour un conteneur, et non pour l'application dans son ensemble. Prism utilise le cadre d'unité pour la sémantique des conteneurs et l'injection de dépendances et, dans l'ensemble, j'aime beaucoup Unity.
En général, il est logique que la fenêtre racine souscrive à cet événement - elle peut ouvrir la boîte de dialogue et définir son contexte de données sur le ViewModel qui est transmis avec un événement déclenché.
Cette configuration permet à ViewModels de demander à l'application d'ouvrir une boîte de dialogue et de réagir aux actions de l'utilisateur sans rien connaître de l'interface utilisateur, de sorte que le MVVM-ness reste globalement complet.
Il y a cependant des moments où l'interface utilisateur doit ouvrir les dialogues, ce qui peut rendre les choses un peu plus difficiles. Considérez par exemple si la position du dialogue dépend de l'emplacement du bouton qui l'ouvre. Dans ce cas, vous devez avoir des informations spécifiques à l’UI lorsque vous demandez l’ouverture d’une boîte de dialogue. Je crée généralement une classe distincte qui contient un ViewModel et des informations pertinentes sur l'interface utilisateur. Malheureusement, certains couplages semblent inévitables là-bas.
Pseudo-code d'un gestionnaire de bouton qui ouvre une boîte de dialogue nécessitant des données de position d'élément:
ButtonClickHandler(sender, args){
var vm = DataContext as ISomeDialogProvider; // check for null
var ui_vm = new ViewModelContainer();
// assign margin, width, or anything else that your custom dialog might require
...
ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
// raise the dialog show event
}
La vue de dialogue se liera aux données de position et passera le ViewModel contenu au ContentControl
intérieur. Le ViewModel lui-même ne sait toujours rien de l'interface utilisateur.
En général, je n'utilise ni la propriété DialogResult
return de la méthode ShowDialog()
ni l'attente du blocage du thread jusqu'à la fermeture de la boîte de dialogue. Un dialogue modal non standard ne fonctionne pas toujours comme ça, et dans un environnement composite, vous ne voulez souvent pas vraiment qu'un gestionnaire d'événements bloque comme ça de toute façon. Je préfère laisser les ViewModels s'en occuper - le créateur d'un ViewModel peut s'abonner à ses événements pertinents, définir des méthodes commit/cancel, etc., de sorte qu'il n'est pas nécessaire de s'appuyer sur ce mécanisme d'interface utilisateur.
Donc au lieu de ce flux:
// in code behind
var result = somedialog.ShowDialog();
if (result == ...
J'utilise:
// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container
Je le préfère de cette façon car la plupart de mes dialogues sont des contrôles pseudo-modaux non bloquants et que le faire de cette manière semble plus simple que de le contourner. Facile à tester aussi bien.
Jason Dolinger a fait un bon screencast de MVVM. Comme Egor l'a mentionné, il n'y a pas un seul bon exemple. Ils sont partout. La plupart sont de bons exemples de MVVM, mais pas lorsque vous abordez des problèmes complexes. Chacun a sa manière. Laurent Bugnion dispose également d'un bon moyen de communication entre les modèles de vue. http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx Cinch est également un bon exemple. Paul Stovel a un bon post qui explique aussi beaucoup avec son framework Magellan.
Trouvé celui-ci utile. A du code aussi.
Avez-vous regardé Caliburn ? L'exemple de ContactManager contient beaucoup de bonnes choses. Les exemples WPF génériques fournissent également une bonne vue d'ensemble des commandes. La documentation est assez bonne et les forums sont actifs. Conseillé!
J'ai également partagé votre frustration. J'écris une application et j'avais ces 3 exigences:
Tout ce que j'ai trouvé étaient des morceaux, alors j'ai juste commencé à l'écrire du mieux que je pouvais. Après y être un peu entré, j'ai compris que d'autres personnes (comme vous) pourraient utiliser une application de référence. J'ai donc reformulé le contenu générique dans un cadre d'application WPF/MVVM et l'ai publié sous la licence LGPL. Je l'ai nommé SoapBox Core . Si vous allez sur la page de téléchargement, vous verrez qu’elle est fournie avec une petite application de démonstration et que le code source de cette application de démonstration est également disponible au téléchargement. J'espère que vous trouvez cela utile. Aussi, écrivez-moi à scott {at} soapboxautomation.com si vous souhaitez plus d’informations.
[~ # ~] modifier [~ # ~] : a également posté un article de CodeProject expliquant comment cela fonctionne.
J'ajoute ici le lien d'une application WPF (Application de gestion d'inventaire) qui utilise une architecture MVVM conçu par moi.
Son interface utilisateur est géniale. https://github.com/shivam01990/InventoryManagement
L'exemple de projet dans le cadre Cinch montre les outils de base CRUD et de navigation. C'est un assez bon exemple d'utilisation de MVVM et inclut un article en plusieurs parties expliquant son utilisation et ses motivations.
J'ai écrit un exemple simple de MVVM à partir de zéro sur le projet de code voici le lien MVVM WPF étape par étape . Cela commence à partir d'une architecture simple à 3 couches et vous permet d'utiliser un framework tel que PRISM.
Même moi, j'ai partagé la frustration jusqu'à ce que je prenne l'affaire entre mes mains. J'ai commencé IncEditor.
IncEditor ( http://inceditor.codeplex.com ) est un éditeur qui tente de présenter les développeurs à WPF, MVVM & MEF. Je l'ai démarré et j'ai réussi à obtenir des fonctionnalités telles que le support "thème". Je ne suis pas un expert en WPF, MVVM ou MEF, je ne peux donc pas y mettre beaucoup de fonctionnalités. Je vous demande sincèrement de le rendre meilleur pour que les nuls comme moi puissent mieux le comprendre.