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.