J'essaie d'apprendre WPF et le problème MVVM, mais j'ai rencontré un problème. Cette question est similaire mais pas tout à fait la même que celle-ci (manipulation-dialogues-en-wpf-avec-mvvm) ...
J'ai un formulaire de «connexion» écrit en utilisant le modèle MVVM.
Ce formulaire a un ViewModel qui contient le nom d'utilisateur et le mot de passe, qui sont liés à la vue dans le XAML à l'aide de liaisons de données normales. Il a également une commande "Login" qui est liée au bouton "Login" sur le formulaire, agan utilisant une liaison de données normale.
Lorsque la commande "Login" se déclenche, elle appelle une fonction dans le ViewModel qui s'éteint et envoie des données sur le réseau pour se connecter. Lorsque cette fonction se termine, il y a 2 actions:
La connexion n'était pas valide - nous montrons juste un MessageBox et tout va bien
La connexion était valide, nous devons fermer le formulaire de connexion et le faire renvoyer vrai comme son
DialogResult
...
Le problème est que le ViewModel ne sait rien de la vue réelle, alors comment peut-il fermer la vue et lui dire de renvoyer un DialogResult particulier ?? Je pourrais coller du code dans le CodeBehind, et / ou passer la vue au ViewModel, mais il semble que cela vaincrait entièrement le point de MVVM ...
Mettre à jour
En fin de compte, je viens de violer la «pureté» du modèle MVVM et j'ai demandé à View de publier un Closed
événement et d'exposer une Close
méthode. Le ViewModel appelle alors simplement view.Close
. La vue n'est connue que via une interface et câblée via un conteneur IOC, donc aucune testabilité ou maintenabilité n'est perdue.
Il semble plutôt idiot que la réponse acceptée soit à -5 voix! Bien que je sois bien conscient des bons sentiments que l'on ressent en résolvant un problème tout en étant "pur", je ne suis sûrement pas le seul à penser que 200 lignes d'événements, de commandes et de comportements juste pour éviter une méthode d'une ligne dans le nom de "motifs" et de "pureté" est un peu ridicule ....
Close
méthode simple est toujours la meilleure solution. Tout le reste sur les autres dialogues plus complexes est MVVM et databound, mais il semblait tout simplement idiot d'implémenter les énormes "solutions" ici au lieu d'une simple méthode ...Réponses:
J'ai été inspiré par la réponse de Thejuan pour écrire une propriété attachée plus simple. Pas de styles, pas de déclencheurs; à la place, vous pouvez simplement faire ceci:
C'est presque aussi propre que si l'équipe WPF avait bien compris et avait fait de DialogResult une propriété de dépendance en premier lieu. Il suffit de mettre une
bool? DialogResult
propriété sur votre ViewModel et d'implémenter INotifyPropertyChanged, et voilà, votre ViewModel peut fermer la fenêtre (et définir son DialogResult) simplement en définissant une propriété. MVVM comme il se doit.Voici le code pour DialogCloser:
J'ai également posté cela sur mon blog .
la source
De mon point de vue, la question est plutôt bonne car la même approche serait utilisée non seulement pour la fenêtre "Login", mais pour tout type de fenêtre. J'ai passé en revue de nombreuses suggestions et aucune ne me convient. Veuillez consulter ma suggestion tirée de l' article sur le modèle de conception MVVM .
Chaque classe ViewModel doit hériter de
WorkspaceViewModel
celle qui a l'RequestClose
événement et laCloseCommand
propriété duICommand
type. L'implémentation par défaut de laCloseCommand
propriété déclenchera l'RequestClose
événement.Afin de fermer la fenêtre, la
OnLoaded
méthode de votre fenêtre doit être remplacée:ou
OnStartup
méthode de votre application:Je suppose que la mise en œuvre des
RequestClose
événements et desCloseCommand
propriétés dans leWorkspaceViewModel
est assez claire, mais je vais les montrer cohérentes:Et le code source de
RelayCommand
:PS Ne me traitez pas mal pour ces sources! Si je les avais hier, cela m'aurait fait gagner quelques heures ...
PPS Tous commentaires ou suggestions sont les bienvenus.
la source
customer.RequestClose
dans le code derrière votre fichier XAML ne viole-t-il pas le modèle MVVM? Vous auriez pu tout aussi bien vous lier auClick
gestionnaire d'événements sur votre bouton de fermeture en voyant que vous avez touché le code derrière de toute façon et que vous avez fait unthis.Close()
! Droite?J'ai utilisé des comportements attachés pour fermer la fenêtre. Liez une propriété "signal" sur votre ViewModel au comportement attaché (j'utilise en fait un déclencheur) Lorsqu'il est défini sur true, le comportement ferme la fenêtre.
http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/
la source
Il y a beaucoup de commentaires faisant valoir les avantages et les inconvénients de MVVM ici. Pour moi, je suis d'accord avec Nir; il s'agit d'utiliser le modèle de manière appropriée et MVVM ne convient pas toujours. Les gens semblent être prêts à sacrifier JUST tous les principes les plus importants de la conception de logiciels pour l'adapter à MVVM.
Cela dit, je pense que votre cas pourrait être un bon ajustement avec un peu de refactoring.
Dans la plupart des cas que j'ai rencontrés, WPF vous permet de vous en tirer SANS plusieurs
Window
s. Vous pourriez peut-être essayer d'utiliserFrame
s etPage
s au lieu de Windows avecDialogResult
s.Dans votre cas, ma suggestion serait de
LoginFormViewModel
gérer leLoginCommand
et si la connexion n'est pas valide, définissez une propriété surLoginFormViewModel
une valeur appropriée (false
ou une valeur d'énumération commeUserAuthenticationStates.FailedAuthentication
). Vous feriez de même pour une connexion réussie (true
ou une autre valeur énumérée). Vous utiliseriez alors unDataTrigger
qui répond aux différents états d'authentification des utilisateurs et pourriez utiliser un simpleSetter
pour changer laSource
propriété duFrame
.Ayant votre fenêtre de connexion renvoyer un
DialogResult
je pense que c'est là que vous êtes confus; c'estDialogResult
vraiment une propriété de votre ViewModel. Dans mon expérience, certes limitée, avec WPF, quand quelque chose ne se sent pas bien, généralement parce que je pense à la façon dont j'aurais fait la même chose dans WinForms.J'espère que cela pourra aider.
la source
En supposant que votre boîte de dialogue de connexion est la première fenêtre créée, essayez ceci dans votre classe LoginViewModel:
la source
Il s'agit d'une solution simple et propre - vous ajoutez un événement au ViewModel et demandez à la fenêtre de se fermer lorsque cet événement est déclenché.
Pour plus de détails, voir mon article de blog, Fermer la fenêtre de ViewModel .
XAML:
ViewModel:
Remarque: L'exemple utilise Prism's
DelegateCommand
(voir Prism: Commanding ), mais n'importe quelleICommand
implémentation peut être utilisée d'ailleurs.Vous pouvez utiliser les comportements de ce package officiel.
la source
La façon dont je le gérerais est d'ajouter un gestionnaire d'événements dans mon ViewModel. Une fois l'utilisateur connecté, je déclencherais l'événement. À mon avis, je m'attacherais à cet événement et lorsqu'il se déclencherait, je fermerais la fenêtre.
la source
Voici ce que j'ai fait au départ, qui fonctionne, mais cela semble plutôt long et laid (tout ce qui est statique global n'est jamais bon)
1: App.xaml.cs
2: LoginForm.xaml
3: LoginForm.xaml.cs
4: LoginFormViewModel.cs
Plus tard, j'ai ensuite supprimé tout ce code et j'ai simplement
LoginFormViewModel
appelé la méthode Close sur sa vue. Cela a fini par être beaucoup plus agréable et plus facile à suivre. À mon humble avis, le motif est de donner aux gens un moyen plus facile de comprendre ce que fait votre application, et dans ce cas, MVVM le rendait beaucoup plus difficile à comprendre que si je ne l'avais pas utilisé, et était maintenant un anti- modèle.la source
Pour info, je suis tombé sur ce même problème et je pense avoir trouvé un moyen de contourner le problème qui ne nécessite pas de variables globales ou statiques, bien que ce ne soit pas la meilleure réponse. Je vous laisse décider vous-même.
Dans mon cas, le ViewModel qui instancie la fenêtre à afficher (appelons-le ViewModelMain) connaît également le LoginFormViewModel (en utilisant la situation ci-dessus comme exemple).
J'ai donc créé une propriété sur le LoginFormViewModel de type ICommand (appelons-la CloseWindowCommand). Ensuite, avant d'appeler .ShowDialog () sur la fenêtre, j'ai défini la propriété CloseWindowCommand sur LoginFormViewModel sur la méthode window.Close () de la fenêtre que j'ai instanciée. Ensuite, à l'intérieur de LoginFormViewModel, tout ce que j'ai à faire est d'appeler CloseWindowCommand.Execute () pour fermer la fenêtre.
C'est un peu une solution de contournement / hack, je suppose, mais cela fonctionne bien sans vraiment casser le modèle MVVM.
N'hésitez pas à critiquer ce processus autant que vous le souhaitez, je peux le prendre! :)
la source
C'est probablement très tard, mais j'ai rencontré le même problème et j'ai trouvé une solution qui fonctionne pour moi.
Je ne peux pas comprendre comment créer une application sans dialogues (c'est peut-être juste un blocage mental). J'étais donc dans une impasse avec MVVM et je montrais une boîte de dialogue. Je suis donc tombé sur cet article CodeProject:
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
Qui est un UserControl qui permet essentiellement à une fenêtre d'être dans l'arborescence visuelle d'une autre fenêtre (non autorisé dans xaml). Il expose également une propriété de dépendance booléenne appelée IsShowing.
Vous pouvez définir un style comme, généralement dans une ressource, qui affiche essentiellement la boîte de dialogue chaque fois que la propriété Content du contrôle! = Null via des déclencheurs:
Dans la vue où vous souhaitez afficher la boîte de dialogue, il vous suffit de présenter ceci:
Et dans votre ViewModel, tout ce que vous avez à faire est de définir la propriété sur une valeur (Remarque: la classe ViewModel doit prendre en charge INotifyPropertyChanged pour que la vue sache que quelque chose s'est produit).
ainsi:
Pour faire correspondre le ViewModel avec la vue, vous devriez avoir quelque chose comme ça dans un spécialiste des ressources:
Avec tout cela, vous obtenez un code à une ligne pour afficher la boîte de dialogue. Le problème que vous obtenez est que vous ne pouvez pas vraiment fermer la boîte de dialogue avec juste le code ci-dessus. C'est pourquoi vous devez mettre un événement dans une classe de base ViewModel dont DisplayViewModel hérite et au lieu du code ci-dessus, écrivez ceci
Ensuite, vous pouvez gérer le résultat de la boîte de dialogue via le rappel.
Cela peut sembler un peu complexe, mais une fois que les bases sont posées, c'est assez simple. Encore une fois, c'est ma mise en œuvre, je suis sûr qu'il y en a d'autres :)
J'espère que cela aide, cela m'a sauvé.
la source
Ok, donc cette question a presque 6 ans et je ne trouve toujours pas ici ce que je pense que c'est la bonne réponse, alors permettez-moi de partager mes "2 cents" ...
J'ai en fait 2 façons de le faire, la première est la plus simple ... la seconde à droite, donc si vous cherchez la bonne, sautez simplement le n ° 1 et passez au n ° 2 :
1. Rapide et facile (mais pas complet)
Si je n'ai qu'un petit projet, je crée parfois simplement une action CloseWindow dans le ViewModel:
Et celui qui crée la vue, ou dans le code de la vue derrière, je viens de définir la méthode que l'action appellera:
(rappelez-vous que MVVM concerne la séparation de la vue et du ViewModel ... le code de la vue est toujours la vue et tant qu'il y a une séparation appropriée, vous ne violez pas le modèle)
Si certains ViewModel créent une nouvelle fenêtre:
Ou si vous le souhaitez dans votre fenêtre principale, placez-le simplement sous le constructeur de votre vue:
lorsque vous souhaitez fermer la fenêtre, il vous suffit d'appeler l'action sur votre ViewModel.
2. La bonne façon
Maintenant, la bonne façon de le faire est d'utiliser Prism (IMHO), et tout à ce sujet peut être trouvé ici .
Vous pouvez faire une demande d'interaction , la remplir avec toutes les données dont vous aurez besoin dans votre nouvelle fenêtre, la déjeuner, la fermer et même recevoir des données . Tout cela encapsulé et approuvé MVVM. Vous obtenez même un état de la façon dont la fenêtre a été fermée , comme si l'utilisateur
Canceled
ouAccepted
(bouton OK) la fenêtre et les données si vous en avez besoin . C'est un peu plus compliqué et réponse n ° 1, mais c'est beaucoup plus complet, et un modèle recommandé par Microsoft.Le lien que j'ai donné contient tous les extraits de code et exemples, donc je ne vais pas prendre la peine de placer du code ici, il suffit de lire l'article de téléchargement du Prism Quick Start et de l'exécuter, c'est vraiment simple à comprendre juste un peu plus verbeux le faire fonctionner, mais les avantages sont plus importants que la simple fermeture d'une fenêtre.
la source
+=
pour ajouter un délégué, et appelez l'action, elle se déclenchera toutes ... Ou vous devez faire une logique spéciale sur votre machine virtuelle afin qu'elle sache quelle fenêtre fermer (peut-être avoir une collection d'actions de fermeture) .... Mais je pense qu'avoir plusieurs vues liées à une machine virtuelle n'est pas la meilleure pratique, c'est mieux gérer pour avoir une vue et une instance de machine virtuelle, liées l'une à l'autre et peut-être une machine virtuelle parent qui gère toutes les machines virtuelles enfants liées à toutes les vues.la source
Vous pouvez demander au ViewModel d'exposer un événement auquel la vue s'enregistre. Ensuite, lorsque le ViewModel décide de l'heure de fermeture de la vue, il déclenche cet événement qui provoque la fermeture de la vue. Si vous voulez qu'une valeur de résultat spécifique soit renvoyée, vous auriez une propriété dans le ViewModel pour cela.
la source
Juste pour ajouter au nombre énorme de réponses, je veux ajouter ce qui suit. En supposant que vous avez une ICommand sur votre ViewModel et que vous souhaitez que cette commande ferme sa fenêtre (ou toute autre action d'ailleurs), vous pouvez utiliser quelque chose comme ce qui suit.
Ce n'est pas parfait, et pourrait être difficile à tester (car il est difficile de se moquer / écraser un statique) mais il est plus propre (à mon humble avis) que les autres solutions.
Erick
la source
J'ai implémenté la solution de Joe White, mais j'ai rencontré des problèmes avec " DialogResult occasionnel ne peut être défini qu'après la création de la fenêtre et l'afficher sous forme de boîte de dialogue ».
Je gardais le ViewModel après la fermeture de la vue et, parfois, j'ai ouvert une nouvelle vue en utilisant la même machine virtuelle. Il semble que la fermeture de la nouvelle vue avant que l'ancienne vue n'ait été récupérée a conduit DialogResultChanged à essayer de définir DialogResult propriété sur la fenêtre fermée, provoquant ainsi l'erreur.
Ma solution était de changer DialogResultChanged pour vérifier la fenêtre propriété IsLoaded de :
Après avoir effectué cette modification, toutes les pièces jointes aux boîtes de dialogue fermées sont ignorées.
la source
J'ai fini par mélanger la réponse de Joe White et du code de la réponse d' Adam Mills , car je devais montrer un contrôle utilisateur dans une fenêtre créée par programme. Donc, le DialogCloser n'a pas besoin d'être sur la fenêtre, il peut être sur le contrôle utilisateur lui-même
Et le DialogCloser trouvera la fenêtre du contrôle utilisateur si elle n'était pas attachée à la fenêtre elle-même.
la source
Le comportement est le moyen le plus pratique ici.
D'une part, il peut être lié au modèle de vue donné (qui peut signaler "fermez le formulaire!")
D'un autre côté, il a accès au formulaire lui-même et peut donc s'abonner aux événements spécifiques au formulaire nécessaires, afficher une boîte de dialogue de confirmation ou autre.
Écrire le comportement nécessaire peut être ennuyeux dès la première fois. Cependant, à partir de maintenant, vous pouvez le réutiliser sur chaque formulaire dont vous avez besoin par un extrait XAML à une ligne exacte. Et si nécessaire, vous pouvez l'extraire en tant qu'assemblage séparé afin qu'il puisse être inclus dans le prochain projet que vous souhaitez.
la source
Pourquoi ne pas simplement passer la fenêtre en paramètre de commande?
C #:
XAML:
la source
Window
type qui n'est pas du tout "MVVM" pur. Voir cette réponse, où la machine virtuelle n'est pas limitée à unWindow
objet.Une autre solution consiste à créer une propriété avec INotifyPropertyChanged dans View Model comme DialogResult, puis dans Code Behind, écrivez ceci:
Le fragment le plus important est
_someViewModel_PropertyChanged
.DialogResultPropertyName
peut être une chaîne de const publiqueSomeViewModel
.J'utilise ce genre d'astuce pour apporter des modifications dans les contrôles de vue au cas où cela serait difficile à faire dans ViewModel. OnPropertyChanged dans ViewModel, vous pouvez faire tout ce que vous voulez dans View. ViewModel est toujours «testable à l'unité» et quelques petites lignes de code dans le code ne font aucune différence.
la source
J'irais comme ça:
la source
J'ai lu toutes les réponses mais je dois dire que la plupart d'entre elles ne sont tout simplement pas assez bonnes ou même pires.
Vous pouvez gérer cela magnifiquement avec la classe DialogService dont la responsabilité est d'afficher la fenêtre de dialogue et de renvoyer le résultat de la boîte de dialogue. J'ai créé un exemple de projet démontrant sa mise en œuvre et son utilisation.
voici les parties les plus importantes:
N'est-ce pas simplement plus simple? plus simple, plus lisible et enfin mais non moins facile à déboguer que EventAggregator ou d'autres solutions similaires?
comme vous pouvez le voir, dans mes modèles de vue, j'ai utilisé la première approche de ViewModel décrite dans mon article ici: Meilleure pratique pour appeler View depuis ViewModel dans WPF
Bien sûr, dans le monde réel, les
DialogService.ShowDialog
doivent avoir plus d'options pour configurer la boîte de dialogue, par exemple les boutons et les commandes à exécuter. Il y a différentes façons de le faire, mais c'est hors de portée :)la source
Bien que cela ne réponde pas à la question de savoir comment le faire via le viewmodel, cela montre comment le faire en utilisant uniquement XAML + le SDK de mélange.
J'ai choisi de télécharger et d'utiliser deux fichiers à partir du SDK Blend, que vous pouvez tous deux sous forme de package de Microsoft via NuGet. Les fichiers sont:
System.Windows.Interactivity.dll et Microsoft.Expression.Interactions.dll
Microsoft.Expression.Interactions.dll vous offre de belles fonctionnalités telles que la possibilité de définir des propriétés ou d'appeler une méthode sur votre modèle de vue ou autre cible et contient également d'autres widgets.
Certains XAML:
Notez que si vous optez simplement pour un comportement OK / Annuler simple, vous pouvez vous en sortir en utilisant les propriétés IsDefault et IsCancel tant que la fenêtre est affichée avec Window.ShowDialog ().
J'ai personnellement eu des problèmes avec un bouton dont la propriété IsDefault était définie sur true, mais elle était masquée lors du chargement de la page. Il ne semblait pas vouloir jouer correctement après avoir été affiché, donc je définis simplement la propriété Window.DialogResult comme indiqué ci-dessus à la place et cela fonctionne pour moi.
la source
Voici la solution simple sans bug (avec le code source), ça marche pour moi.
Dérivez votre ViewModel de
INotifyPropertyChanged
Créer une propriété observable CloseDialog dans ViewModel
}
Attacher un gestionnaire dans View pour cette modification de propriété
Vous avez maintenant presque terminé. Dans le gestionnaire d'événements,
DialogResult = true
la source
Créez un
Dependency Property
dans votreView
/ anyUserControl
(ouWindow
vous souhaitez fermer). Comme ci-dessous:Et liez-le à partir de la propriété de votre ViewModel :
Propriété dans
VeiwModel
:Déclenchez maintenant l'opération de fermeture en modifiant la
CloseWindow
valeur dans ViewModel. :)la source
Où vous devez fermer la fenêtre, mettez-la simplement dans le modèle de vue:
ta-da
la source
C'est assez!
la source