C'est mon humble avis sur MVP et vos problèmes spécifiques.
Tout d'abord , tout ce avec lequel un utilisateur peut interagir ou simplement être affiché est une vue . Les lois, le comportement et les caractéristiques d'une telle vue sont décrits par une interface . Cette interface peut être implémentée à l'aide d'une interface utilisateur WinForms, d'une interface utilisateur de 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 n'a pas d'importance tant qu'elle obéit aux lois de son interface d'affichage .
Deuxièmement , 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 l'implémentation de la vue concrète 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 vraiment aucun gain à avoir la vue en sachant quoi que ce soit sur son présentateur. Il y a un contrat convenu entre le présentateur et la vue et c'est indiqué par l'interface de vue.
Les implications de Third sont:
- Le présentateur ne dispose d'aucune méthode que la vue peut appeler, mais la vue contient des événements auxquels le présentateur peut s'abonner.
- Le présentateur connaît son point de vue. Je préfère accomplir cela avec l'injection du constructeur sur le présentateur concret.
- La vue n'a aucune idée de quel présentateur la contrôle; il ne sera tout simplement jamais fourni de présentateur.
Pour votre problème, ce qui précède pourrait ressembler à ceci dans un 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 IView
interface de base dans laquelle je cache la Show()
vue du propriétaire ou le titre de vue dont mes vues bénéficient généralement.
À vos questions:
1. Lorsque le winform se charge, il doit obtenir une arborescence. Ai-je raison de penser que la vue devrait donc appeler une méthode telle que: presenter.gettree (), celle-ci déléguera à son tour au modèle, qui obtiendra les données de l'arborescence, la créera et la configurera, la renverra au présentateur, qui à son tour passera à la vue qui l'attribuera 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, car j'ai également 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 selon lequel il veut des données tabulaires.
3. Mon application comporte un certain nombre de classes de modèles avec le même assemblage. Il prend également en charge une architecture de plugins 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 une méthode qui charge les plugins et afficherait les informations 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, les vues doivent les connaître, mais pas le présentateur. S'ils concernent uniquement les données et le modèle, la vue ne devrait rien avoir à voir 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 nœud de l'arborescence à la taille de la grille de données, etc.?
Oui. Considérez-le comme le présentateur fournissant du XML qui décrit les données et la vue qui prend les données et leur applique une feuille de style CSS. Concrètement, le présentateur peut appeler IRoadMapView.SetRoadCondition(RoadCondition.Slippery)
et la vue rend alors la route en rouge.
Qu'en est-il des données pour les nœuds cliqués?
5. Si, lorsque je clique sur les treenodes, devrais-je passer par le nœud spécifique au présentateur, puis à partir de là, le présentateur déterminerait les données dont il a besoin et demandait ensuite au modèle ces données, avant de les présenter à la vue?
Si possible, je transmettrais toutes les données nécessaires pour présenter l'arbre dans une vue en un seul coup. Mais si certaines données sont trop volumineuses pour être transmises depuis le début ou si elles sont de nature dynamique et ont besoin du «dernier instantané» du modèle (via le présentateur), alors j'ajouterais quelque chose comme event LoadNodeDetailsEventHandler LoadNodeDetails
l'interface de vue, de sorte que le Le présentateur peut s'y abonner, récupérer les détails du nœud dans LoadNodeDetailsEventArgs.Node
(éventuellement via son identifiant quelconque) à partir du modèle, de sorte que la vue puisse mettre à jour les détails de son nœud affiché lorsque le délégué du gestionnaire d'événements revient. Notez que des modèles asynchrones peuvent être nécessaires si la récupération des données peut être trop lente pour une bonne expérience utilisateur.