web-dev-qa-db-fra.com

Comment puis-je appliquer le modèle MVC à une application C # WinForms?

Je suis un développeur C++ qui utilise depuis le modèle MVC pour concevoir des interfaces graphiques.

Récemment, je voulais revenir en C #, et j'ai installé une application Windows Forms, mais maintenant je suis un peu perdu sur la façon de le pousser vers une structure compatible MVC.

Ce que j'essaie actuellement de faire, c'est de "déclarer" la classe qui m'est donnée pour les WinForms en tant que vue, et d'ajouter une classe pour le modèle et le contrôleur en arrière-plan. Cependant, je ne suis pas vraiment sûr de la façon d'interagir avec les événements, comme un clic sur un bouton. Habituellement, je redirige ces événements vers le contrôleur et exécute une action sur la vue une fois que j'ai terminé.

Cela semble assez insatisfaisant dans cette constellation. Par exemple, si je voulais implémenter un bouton "Quitter", je devrais rediriger l'événement de la vue vers le contrôleur, ainsi que mettre en œuvre une méthode publique supplémentaire dans ma vue qui peut ensuite être appelée depuis le contrôleur, tandis que je pourrait simplement appeler Close () à partir de la vue dans la première instance.

Avez-vous des conseils pour moi? Ma compréhension des formulaires Windows en C # n'est-elle pas encore assez bonne pour essayer une implémentation MVC? Suis-je en train de donner un mauvais rôle à la classe de formulaires? MVC est-il simplement une architecture inappropriée pour ce cas d'utilisation?

11
Sossenbinder

Par coïncidence, je travaille sur un projet WinForms qui est calqué sur MVC. Je n'appellerais pas cela une réponse parfaite, mais je vais expliquer ma conception globale et j'espère que cela peut vous aider à trouver votre propre.

Sur la base de la lecture que j'ai faite avant de commencer ce projet, il ne semble pas y avoir de "bonne" façon de mettre cela en œuvre. J'ai suivi les simples principes de conception OOP et MVC et le reste était un essai et une erreur pendant que je développais un flux de travail.

MVC est-il simplement une architecture inappropriée pour ce cas d'utilisation?

Non ..? Il n'y a pas assez de contexte dans votre question pour y répondre directement. Pourquoi utilisez-vous MVC en premier lieu? Quelles sont les exigences non fonctionnelles de votre projet? Votre projet va-t-il être très lourd en interface utilisateur? Vous vous souciez davantage de la sécurité et préférez une architecture en couches? Quelles sont les principales composantes de votre projet? Peut-être que chaque composant a besoin d'un modèle de conception différent. Découvrez pourquoi vous souhaitez utiliser ce modèle de conception en premier lieu et vous pourriez répondre à votre propre question;)

Ma raison d'utiliser MVC: c'est un modèle de conception assez simple à comprendre à mon avis et ma conception est fortement basée sur l'interaction avec l'utilisateur. La façon dont MVC permet au développeur de séparer ses préoccupations est également suffisante pour mon application. Cela rend mon code beaucoup plus maintenable et testable.

Je suppose également que j'utilise davantage une conception hybride. Habituellement, le concept idéal présenté en génie logiciel ne se concrétise pas dans la pratique. Vous pouvez modifier la conception en fonction des besoins de votre projet. Pas besoin de se laisser entraîner dans ce qui est bien ou mal. Il existe des pratiques générales, mais les règles peuvent toujours être pliées ou brisées tant que vous ne vous tirez pas une balle dans le pied.

Mon implémentation a commencé par une conception de haut niveau qui m'a donné une idée des composants dont j'ai besoin. Il est préférable de commencer de cette façon et de progresser dans l'architecture. Voici le diagramme de package pour le projet (créé dans StarUML): enter image description here

Notez que chaque couche, à l'exception de la couche de présentation, dépend du système de messagerie. Il s'agit d'un "langage" commun que les couches inférieures et les sous-systèmes de ces couches utilisent pour communiquer entre eux. Dans mon cas, il s'agissait d'une simple énumération basée sur des opérations pouvant être effectuées. Ce qui m'amène au point suivant...

Considérez les opérations ou les commandes comme la base de votre implémentation. Que voulez-vous que votre application fasse? Décomposez-le en opérations les plus fondamentales. Par exemple: CreateProject, WriteNotes, SaveProject, LoadProject etc. L'interface graphique (ou les classes de formulaire) va se produire un événement (comme une pression sur un bouton). Chaque opération est associée à une méthode de contrôleur. Dans ce cas, quelque chose comme Exit est trop simple. L'application peut simplement être fermée à partir de la classe Form. Mais supposons que je veuille d'abord conserver certaines données d'application dans un fichier? J'appellerai la méthode "Save" de la classe de contrôleur respective dans ma méthode de pression de bouton.

À partir de là, le contrôleur appellera le bon ensemble d'opérations à partir des classes de service. Les classes de service dans mon application agissent comme une interface avec la couche domaine. Ils valideront les entrées reçues de l'appel de méthode du contrôleur (et donc de l'interface graphique) et manipuleront le modèle de données.

Une fois la validation et la manipulation d'objet correspondante terminées, la méthode de service renvoie un code de message au contrôleur. Par exemple, MessageCodes.SaveSuccess. Les classes de contrôleur et de service étaient basées sur les objets de domaine et/ou l'ensemble général d'opérations pouvant être regroupées.

Par exemple: FileMenuController (opérations: NewProject, SaveProject, LoadProject) -> ProjectServices (CreateProject, PersistProjectToFile, LoadProjectFromFile). Où Project serait une classe de domaine dans votre modèle de données. Les classes Controller et Service dans mon cas étaient des classes non instanciables avec des méthodes statiques.

Ensuite, le contrôleur reconnaît que l'opération s'est terminée avec succès/échec. Désormais, le contrôleur dispose de son propre système de messagerie qu'il utilise pour interagir avec la couche de présentation, d'où la double dépendance entre les couches Service et Présentation. Dans ce cas, une classe appelée ViewState dans le package ViewModels est toujours renvoyée à l'interface graphique par le contrôleur. Cet état contient des informations telles que: " l'état dans lequel vous avez essayé de mettre l'application en cours de validité?", " un message lisible par l'utilisateur sur l'opération que vous avez tenté d'effectuer et pourquoi elle l'a été ou n'a pas réussi (messages d'erreur) "et une classe ViewModel.

La classe ViewModel contient les données pertinentes de la couche de domaine que l'interface graphique utilisera pour mettre à jour la vue. Ces modèles de vues ressemblent aux classes de domaine mais dans mon cas j'ai utilisé des objets très skinny. Fondamentalement, ils n'ont pratiquement aucun comportement, relaient simplement des informations sur l'état de niveau inférieur de l'application. En d'autres termes, je [~ # ~] ne vais jamais [~ # ~] donner mes classes de domaine à la couche de présentation. C'est aussi pourquoi les packages Controllers et Services divisent la couche de service en deux parties. Les contrôleurs ne géreront jamais les classes de domaine ni ne valideront leur état. Ils agissent simplement comme une frontière qui convertit les données pertinentes de l'interface graphique en données pertinentes pour le domaine que les services peuvent utiliser et vice-versa. L'inclusion de la logique de service dans le contrôleur conduirait à des contrôleurs très fat, qui sont plus difficiles à maintenir.

J'espère que cela vous donne un point de départ.

3
Alluring Topaz