88 votes

Modèle-Vue-Présentateur dans WinForms

J'essaie de mettre en œuvre la méthode MVP pour la première fois, en utilisant WinForms.

J'essaie de comprendre la fonction de chaque couche.

Dans mon programme, j'ai un bouton de l'interface graphique qui, lorsqu'on clique dessus, ouvre une fenêtre de dialogue d'ouverture.

Ainsi, en utilisant MVP, l'interface graphique gère l'événement de clic du bouton et appelle ensuite presenter.openfile() ;

Dans Presenter.openfile(), doit-il déléguer l'ouverture de ce fichier à la couche modèle, ou, comme il n'y a pas de données ou de logique à traiter, doit-il simplement agir sur la demande et ouvrir la fenêtre openfiledialog ?

Mise à jour : J'ai décidé d'offrir une prime car je pense avoir besoin d'une aide supplémentaire à ce sujet, et de préférence adaptée à mes points spécifiques ci-dessous, afin que je dispose d'un contexte.

Bon, après avoir lu des articles sur les MVP, j'ai décidé de mettre en œuvre la vue passive. En fait, j'aurai un tas de contrôles sur un formulaire Win qui seront gérés par un présentateur, puis les tâches seront déléguées au(x) modèle(s). Mes points spécifiques sont les suivants :

  1. Lorsque la winform se charge, elle doit obtenir une arborescence. Ai-je raison de penser que la vue devrait donc appeler une méthode telle que : presenter.gettree(), qui à son tour sera déléguée au modèle, qui obtiendra les données pour le treeview, le créera et le configurera, le renverra au presenter, qui à son tour le passera à la vue qui l'assignera alors simplement à, disons, un panneau ?

  2. Est-ce que ce serait la même chose pour n'importe quel contrôle de données sur le Winform, puisque j'ai aussi un datagridview ?

  3. Mon application a un certain nombre de classes de modèles avec le même assemblage. Elle prend également en charge une architecture de plugins avec des plugins qui doivent être chargés au démarrage. La vue appelle-t-elle simplement une méthode de présentateur, qui à son tour appelle une méthode qui charge les plugins et affiche les informations dans la vue ? Lequel niveau contrôlerait alors les références des plugins. La vue ou le présentateur ?

  4. Ai-je raison de penser que la vue doit gérer tous les aspects de la présentation, de la couleur des nœuds de l'arborescence à la taille de la grille de données, etc.

Je pense qu'il s'agit de mes principales préoccupations et si je comprends comment le flux devrait être pour ces derniers, je pense que tout ira bien.

121voto

Johann Gerell Points 10649

C'est mon humble avis sur MVP et vos problèmes spécifiques.

Premier tout ce avec quoi l'utilisateur peut interagir, ou simplement être montré, est un outil d'aide à la décision. voir . Les lois, le comportement et les caractéristiques d'une telle vue sont décrits par une interface . Cette interface peut être mise en œuvre à l'aide d'une interface utilisateur WinForms, d'une interface utilisateur console, d'une interface utilisateur Web ou même d'aucune interface utilisateur (généralement lors du test d'un présentateur) - l'implémentation concrète importe peu tant qu'elle obéit aux lois de son interface de vue.

Deuxièmement une vue est toujours contrôlée par une présentateur . Les lois, le comportement et les caractéristiques d'un tel présentateur sont également décrits par un interface . Cette interface n'a aucun intérêt dans l'implémentation concrète de la vue tant qu'elle obéit aux lois de son interface de vue.

Troisièmement Comme un présentateur contrôle sa vue, pour minimiser les dépendances, il n'y a pas vraiment d'intérêt à ce que la vue sache quoi que ce soit sur son présentateur. Il existe un contrat entre le présentateur et la vue, qui est défini par l'interface de la vue.

Les implications de Troisièmement sont :

  • Le présentateur n'a pas de méthodes que la vue peut appeler, mais la vue a des événements auxquels le présentateur peut souscrire.
  • Le présentateur connaît son point de vue. Je préfère accomplir ceci avec l'injection de constructeur sur le présentateur concret.
  • La vue n'a aucune idée du présentateur qui la contrôle ; il ne lui sera simplement jamais fourni de présentateur.

Pour votre problème, ce qui précède pourrait ressembler à ceci en code quelque peu simplifié :

interface IConfigurationView
{
    event EventHandler SelectConfigurationFile;

    void SetConfigurationFile(string fullPath);
    void Show();
}

class ConfigurationView : IConfigurationView
{
    Form form;
    Button selectConfigurationFileButton;
    Label fullPathLabel;

    public event EventHandler SelectConfigurationFile;

    public ConfigurationView()
    {
        // UI initialization.

        this.selectConfigurationFileButton.Click += delegate
        {
            var Handler = this.SelectConfigurationFile;

            if (Handler != null)
            {
                Handler(this, EventArgs.Empty);
            }
        };
    }

    public void SetConfigurationFile(string fullPath)
    {
        this.fullPathLabel.Text = fullPath;
    }

    public void Show()
    {
        this.form.ShowDialog();        
    }
}

interface IConfigurationPresenter
{
    void ShowView();
}

class ConfigurationPresenter : IConfigurationPresenter
{
    Configuration configuration = new Configuration();
    IConfigurationView view;

    public ConfigurationPresenter(IConfigurationView view)
    {
        this.view = view;            
        this.view.SelectConfigurationFile += delegate
        {
            // The ISelectFilePresenter and ISelectFileView behaviors
            // are implicit here, but in a WinForms case, a call to
            // OpenFileDialog wouldn't be too far fetched...
            var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
            selectFilePresenter.ShowView();
            this.configuration.FullPath = selectFilePresenter.FullPath;
            this.view.SetConfigurationFile(this.configuration.FullPath);
        };
    }

    public void ShowView()
    {
        this.view.SetConfigurationFile(this.configuration.FullPath);
        this.view.Show();
    }
}

En plus de ce qui précède, j'ai généralement une base IView interface où je planque les Show() et toute vue de propriétaire ou titre de vue dont mes vues bénéficient habituellement.

A vos questions :

1. Lorsque la winform se charge, elle doit obtenir une arborescence. Ai-je raison de penser que la vue devrait donc appeler une méthode telle que : presenter.gettree(), qui à son tour sera déléguée au modèle, qui obtiendra les données pour le treeview, le créera et le configurera, le renverra au presenter, qui à son tour le passera à la vue qui l'assignera alors simplement à, disons, un panneau ?

J'appellerais IConfigurationView.SetTreeData(...) de IConfigurationPresenter.ShowView() juste avant l'appel à IConfigurationView.Show()

2. Est-ce que ce serait la même chose pour n'importe quel contrôle de données sur le Winform, puisque j'ai aussi un datagridview ?

Oui, j'appellerais IConfigurationView.SetTableData(...) pour ça. C'est à la vue de formater les données qui lui sont données. Le présentateur obéit simplement au contrat de la vue qui veut des données tabulaires.

3. Mon application a un certain nombre de classes de modèles avec le même assemblage. Elle prend également en charge une architecture de plugins avec des plugins qui doivent être chargés au démarrage. La vue appelle-t-elle simplement une méthode de présentateur, qui à son tour appelle une méthode qui charge les plugins et affiche les informations dans la vue ? Quel niveau contrôlerait alors les références des plugins. La vue ou le présentateur ?

Si les plugins sont liés à la vue, alors les vues doivent les connaître, mais pas le présentateur. S'ils concernent les données et le modèle, alors la vue ne devrait rien avoir à faire avec eux.

4. Ai-je raison de penser que la vue doit gérer tous les aspects de la présentation, de la couleur des nœuds de l'arborescence à la taille de la grille de données, etc.

Oui. Pensez-y comme le présentateur qui fournit le XML décrivant les données et la vue qui prend les données et leur applique une feuille de style CSS. Concrètement, le présentateur pourrait appeler IRoadMapView.SetRoadCondition(RoadCondition.Slippery) et la vue rend alors la route en couleur rouge.

Qu'en est-il des données pour les nœuds cliqués ?

5. Si je clique sur les noeuds verts, dois-je faire passer le noeud spécifique au présentateur et, à partir de là, le présentateur détermine les données dont il a besoin et les demande au modèle, avant de les présenter à la vue ?

Si possible, je passerais toutes les données nécessaires pour présenter l'arbre dans une vue en une seule fois. Mais si certaines données sont trop volumineuses pour être transmises dès le début ou si elles sont dynamiques par nature et nécessitent le "dernier instantané" du modèle (via le présentateur), j'ajouterais quelque chose comme event LoadNodeDetailsEventHandler LoadNodeDetails à l'interface de vue, afin que le présentateur puisse s'y abonner, récupérer les détails du nœud dans l'interface de vue. LoadNodeDetailsEventArgs.Node (éventuellement par le biais de son ID) du modèle, afin que la vue puisse mettre à jour les détails du nœud affiché lorsque le délégué du gestionnaire d'événements revient. Notez que des modèles asynchrones de ceci peuvent être nécessaires si la récupération des données est trop lente pour une bonne expérience utilisateur.

10voto

Peter Lillevold Points 20689

Le présentateur, qui contient tous les logique dans la vue, devrait répondre au clic sur le bouton comme @JochemKempe dit . En termes pratiques, le gestionnaire d'événement de clic de bouton appelle presenter.OpenFile() . Le présentateur est alors en mesure de déterminer ce qu'il convient de faire.

S'il décide que l'utilisateur doit sélectionner un fichier, il appels de retour dans la vue (via une interface de vue) et laisser la vue, qui contient tous les détails techniques de l'interface utilisateur, afficher l'écran de l'utilisateur. OpenFileDialog . Il s'agit d'une distinction très importante dans la mesure où le présentateur ne doit pas être autorisé à effectuer des opérations liées à la technologie de l'interface utilisateur utilisée.

Le fichier sélectionné est alors renvoyé au diffuseur qui poursuit sa logique. Cela peut impliquer n'importe quel modèle ou service devant traiter le fichier.

La raison principale de l'utilisation d'un modèle MVP, à mon avis, est de séparer la technologie de l'interface utilisateur de la logique d'affichage. Ainsi, le présentateur orchestre toute la logique tandis que la vue la sépare de la logique de l'interface utilisateur. Cela a l'effet secondaire très agréable de rendre le présentateur entièrement testable à l'unité.

Mise à jour : puisque le présentateur est l'incarnation de la logique trouvée dans une vue spécifique la relation entre la vue et le présentateur est une relation biunivoque de l'OMI. Et à toutes fins utiles, une instance de vue (disons un formulaire) interagit avec une instance de présentateur, et une instance de présentateur interagit avec une seule instance de vue.

Cela dit, dans ma mise en œuvre de MVP avec WinForms, le présentateur interagit toujours avec la vue par le biais d'une interface représentant les capacités de l'interface utilisateur de la vue. Il n'y a pas de limitation sur la vue qui implémente cette interface, ainsi différents "widgets" peuvent implémenter la même interface de vue et réutiliser la classe du présentateur.

2voto

JochemKempe Points 186

Le présentateur doit agir sur la demande et afficher la fenêtre openfiledialog comme vous l'avez suggéré. Puisqu'aucune donnée n'est requise du modèle, le présentateur peut, et doit, traiter la demande.

Supposons que vous ayez besoin des données pour créer certaines entités dans votre modèle. Vous pouvez soit passer le flux à la couche d'accès où vous avez une méthode pour créer des entités à partir du flux, mais je vous suggère de gérer l'analyse du fichier dans votre présentateur et d'utiliser un constructeur ou une méthode Create par entité dans votre modèle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X