Modèle-Vue-présentateur dans WinForms
j'essaie d'implémenter 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 GUI qui, lorsqu'il est cliqué, ouvre une fenêtre openfiledialog.
ainsi, en utilisant MVP, L'interface graphique gère le bouton click event et ensuite appelle presenter.openfile ();
dans presenter.openfile (), devrait-il alors déléguer l'ouverture de ce fichier à la couche model, ou comme il n'y a pas de données ou de logique à traiter, devrait-il simplement agir sur la demande et ouvrir la fenêtre openfiledialog?
mise à Jour: j'ai décidé d'offrir une prime que j'ai besoin de plus d'assistance sur ce point, et de préférence adapté à mes points spécifiques ci-dessous, de sorte que j'ai contexte.
OK, après avoir lu sur MVP, j'ai décidé d'implémenter la vue Passive. Effectivement je vais avoir une série de commandes sur un Winform qui sera manipulé par un Présentateur et puis les tâches déléguées au Modèle(s). Mes points spécifiques sont les suivants:
-
lorsque le winform charge, il doit obtenir un treeview. Ai-je raison de penser que le point de vue devrait donc appeler une méthode telle que: Présentateur.gettree (), ce qui à son tour va déléguer au model, qui va obtenir les données pour la vue sur l'arbre, le créer et le configurer, le retourner au présentateur, qui à son tour va passer à la vue qui sera alors tout simplement céder à, disons, un panneau?
-
est-ce que ce serait la même chose pour n'importe quel contrôle de données sur le Winform, comme j'ai aussi un datagridview?
-
Mon Application, a un certain nombre de classes du modèle avec la même assemblée. Il supporte également une architecture de plugin avec des plugins qui doivent être chargés au démarrage. La vue appellerait-elle simplement une méthode de présentateur, qui à son tour appellerait-on une méthode qui charge les plugins et affiche l'information dans la vue? Quel niveau contrôlerait alors les références du plugin. La vue contiendrait-elle des références à eux ou au présentateur?
-
ai-je raison de penser que la vue doit gérer tout ce qui concerne la présentation, de la couleur du noeud de treeview à la taille de la datagrid, etc.?
je pense qu'ils sont mes principales préoccupations et si je comprenez comment le flux devrait être pour ceux-ci je pense que je vais aller bien.
3 réponses
c'est mon humble avis sur MVP et vos problèmes spécifiques.
First , tout ce qu'un utilisateur peut interagir avec, ou juste être affiché, est un view . Les lois, le comportement et les caractéristiques d'une telle vue est décrite par une interface . Cette interface peut être implémentée en utilisant une interface de WinForms, une interface de console, une interface web ou même pas D'interface du tout (généralement lors de la mise à l'essai d'un présentateur) - le concret la mise en œuvre n'a pas d'importance tant qu'elle respecte les lois de son interface de vue.
Second , une vue est toujours contrôlée par un présentateur . Les lois, le comportement et les caractéristiques d'un tel présentateur sont également décrits par une "interface . Cette interface n'a aucun intérêt dans la mise en œuvre d'une vue concrète tant qu'elle respecte les lois de son interface de vue.
Third , puisqu'un présentateur contrôle sa vue, pour minimiser les dépendances, il n'y a vraiment aucun avantage à avoir la vue en sachant quoi que ce soit sur son présentateur. Il y a un contrat entre le présentateur et la vue, et c'est ce que dit l'interface de la vue.
les conséquences de La Troisième sont:
- le présentateur n'a aucune méthode que la vue peut appeler, mais la vue a é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 de béton.
- Le point de vue n'a aucune idée de ce que le présentateur est en a le contrôle; il va tout simplement de ne jamais être assuré de l'animateur.
Pour votre question, le ci-dessus pourrait ressembler à ceci un peu simplifié le code:
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 habituellement une interface de base IView
où je planque le Show()
et n'importe quelle vue de propriétaire ou de titre de vue dont mes vues bénéficient habituellement.
à vos questions:
1. lorsque le winform charge, il doit obtenir un treeview. Ai-je raison de penser que le point de vue devrait donc appeler une méthode telle que: Présentateur.gettree (), qui à son tour va déléguer au modèle, qui va obtenir les données pour le treeview, le créer et le configurer, le retourner au présentateur, qui à son tour passera à la vue qui va ensuite simplement l'assigner, disons, à un panneau?
j'appellerai
IConfigurationView.SetTreeData(...)
deIConfigurationPresenter.ShowView()
, juste avant l'appel àIConfigurationView.Show()
2. serait-ce la même chose pour n'importe quel contrôle de données sur le Winform, comme j'ai aussi un datagridview?
Oui, j'appellerais
IConfigurationView.SetTableData(...)
pour ça. C'est à la vue de mettre en forme les données. Le présentateur obéit simplement au contrat de la vue qu'il veut des données tabulaires.
3. Mon Application, a un certain nombre de classes du modèle avec la même assemblée. Il supporte également une architecture de plugin avec des plugins qui doivent être chargés au démarrage. Serait le point de vue il suffit d'appeler une méthode de presenter, qui à son tour appellerait une méthode qui charge les plugins et affiche l'information dans la vue? Quel niveau contrôlerait alors les références du plugin. La vue contiendrait-elle des références à eux ou au présentateur?
si les plugins sont liés à la vue, alors les vues devraient être au courant, mais pas le présentateur. S'ils sont tous sur les données et le modèle, alors la vue ne devrait pas avoir quoi que ce soit à faire avec eux.
4. ai-je raison de penser que la vue doit gérer tout ce qui concerne la présentation, de la couleur du noeud de treeview à la taille de la datagrid, etc.?
Oui. Pensez - y comme le présentateur fournissant XML qui décrit les données et la vue qui prend les données et applique une feuille de style CSS à elle. En termes concrets, le présentateur pourrait appeler
IRoadMapView.SetRoadCondition(RoadCondition.Slippery)
et le vue rend alors la route en couleur rouge.
Qu'en est-il des données pour les noeuds cliqués?
5. Si lorsque je clique sur les treenodes, devrais-je passer par le noeud spécifique au présentateur et puis à partir de ce que le présentateur travaillerait sur quelles données il a besoin et demande ensuite le modèle pour ces données, avant de les présenter de nouveau à la vue?
si possible, je passerais toutes les données nécessaires pour présenter l'arbre dans une vue en une prise. Mais si certaines données sont trop volumineuses pour être transmises depuis le début ou si elles sont de nature dynamique et nécessitent le "dernier snapshot" du modèle (via le présentateur), alors je voudrais ajouter quelque chose comme
event LoadNodeDetailsEventHandler LoadNodeDetails
à l'interface de vue, de sorte que le présentateur puisse s'y abonner, récupérer les détails du noeud dansLoadNodeDetailsEventArgs.Node
(éventuellement via son ID de quelque sorte) à partir du modèle, de sorte que la vue peut mettre à jour les détails de ses noeuds affichés lorsque le gestionnaire d'événements delegate retourne. Notez que des modèles asynchrones de ceci pourraient être nécessaires si la collecte des données pourrait être trop lente pour une bonne expérience utilisateur.
le présentateur, qui contient tout logique dans la vue, devrait répondre au bouton cliqué comme @JochemKempe dit . En termes pratiques, le bouton click event handler appelle presenter.OpenFile()
. Le présentateur est alors en mesure de déterminer ce qui doit être fait.
S'il décide que l'utilisateur doit sélectionner un fichier, il rappelle dans la vue (via une interface de vue) et laisser la vue, qui contient toutes les caractéristiques techniques de L'interface utilisateur, affichez le OpenFileDialog
. Il s'agit là d'une distinction très importante, en ce sens que le présentateur ne devrait pas être autorisé à effectuer des opérations liées à la technologie de L'IU utilisée.
le fichier sélectionné sera alors retourné au présentateur qui poursuit sa logique. Cela peut impliquer n'importe quel modèle ou service qui devrait traiter le dossier.
la raison principale pour utiliser un modèle MVP, imo est de séparer la technologie UI du point de vue de la logique. Ainsi, le présentateur orchestre toute 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 unité.
mise à Jour: depuis le présentateur est l'incarnation de la logique trouvée dans le un point de vue spécifique , le view-presenter relation est de l'OMI, un one-to-one relation. Et à toutes fins pratiques, une instance de vue (un Formulaire) interagit avec un présentateur exemple, et un présentateur instance interagit avec une seule instance de vue.
cela dit, dans mon implémentation de MVP avec WinForms le présentateur interagit toujours avec la vue à travers une interface représentant les capacités de L'interface utilisateur de la vue. Il n'y a aucune limitation sur ce que view implémente cette interface, donc différents "widgets" peuvent implémenter la même interface de vue et réutiliser la classe presenter.
le présentateur doit agir sur la demande et afficher la fenêtre openfiledialog comme vous l'avez suggéré. Comme aucune donnée n'est requise du modèle, le présentateur peut, et doit, traiter la requête.
supposons que vous ayez besoin des données pour créer des entités dans votre modèle. Vous pouvez soit passer le ruisseau creux à la couche d'accès où vous avez une méthode pour créer des entités de la rivière, mais je vous suggère de gérer l'analyse du fichier dans votre animateur et l'utilisation d'un constructeur ou méthode de création par entité dans votre modèle.