Dies ist meine bescheidene Einstellung zu MVP und Ihren spezifischen Problemen.
Erstens ist alles, mit dem ein Benutzer interagieren oder nur angezeigt werden kann, eine Ansicht . Die Gesetze, Verhaltensweisen und Eigenschaften einer solchen Ansicht werden durch eine Schnittstelle beschrieben . Diese Schnittstelle kann mithilfe einer WinForms-Benutzeroberfläche, einer Konsolen-Benutzeroberfläche, einer Web-Benutzeroberfläche oder gar keiner Benutzeroberfläche implementiert werden (normalerweise beim Testen eines Präsentators). Die konkrete Implementierung spielt keine Rolle, solange sie den Gesetzen der Ansichtsoberfläche entspricht .
Zweitens wird eine Ansicht immer von einem Präsentator gesteuert . Die Gesetze, Verhaltensweisen und Eigenschaften eines solchen Präsentators werden auch durch eine Schnittstelle beschrieben . Diese Schnittstelle hat kein Interesse an der konkreten Ansichtsimplementierung, solange sie den Gesetzen ihrer Ansichtsschnittstelle entspricht.
Drittens , da ein Präsentator seine Ansicht kontrolliert, um Abhängigkeiten zu minimieren, ist es wirklich kein Vorteil, wenn die Ansicht überhaupt etwas über seinen Präsentator weiß. Es gibt einen vereinbarten Vertrag zwischen dem Präsentator und der Ansicht, der von der Ansichtsoberfläche angegeben wird.
Die Implikationen von Third sind:
- Der Präsentator verfügt über keine Methoden, die die Ansicht aufrufen kann, aber die Ansicht enthält Ereignisse, die der Präsentator abonnieren kann.
- Der Moderator kennt seine Sichtweise. Ich ziehe es vor, dies mit einer Konstruktorinjektion auf dem Betonpräsentator zu erreichen.
- Die Ansicht hat keine Ahnung, welcher Moderator sie steuert. Es wird einfach nie ein Moderator zur Verfügung gestellt.
Für Ihr Problem könnte das Obige in etwas vereinfachtem Code so aussehen:
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();
}
}
Darüber hinaus habe ich normalerweise eine Basisschnittstelle IView
, in der ich die Show()
Eigentümeransicht oder den Ansichtstitel, von dem meine Ansichten normalerweise profitieren, aufbewahre.
Auf Ihre Fragen:
1. Wenn die Winform geladen wird, muss sie eine Baumansicht erhalten. Habe ich Recht, wenn ich denke, dass die Ansicht daher eine Methode wie: presenter.gettree () aufrufen sollte, die wiederum an das Modell delegiert, das die Daten für die Baumansicht abruft, sie erstellt und konfiguriert und an die zurückgibt Moderator, der wiederum an die Ansicht übergeht, die sie dann beispielsweise beispielsweise einem Panel zuweist?
Ich würde rufen IConfigurationView.SetTreeData(...)
aus IConfigurationPresenter.ShowView()
, direkt vor dem AufrufIConfigurationView.Show()
2. Wäre dies für jede Datensteuerung auf der Winform gleich, da ich auch eine Datagrid-Ansicht habe?
Ja, das würde ich fordern IConfigurationView.SetTableData(...)
. Es liegt an der Ansicht, die ihm gegebenen Daten zu formatieren. Der Präsentator befolgt einfach den Vertrag der Ansicht, wonach Tabellendaten gewünscht werden.
3. Meine App verfügt über eine Reihe von Modellklassen mit derselben Assembly. Es unterstützt auch eine Plugin-Architektur mit Plugins, die beim Start geladen werden müssen. Würde die Ansicht einfach eine Präsentationsmethode aufrufen, die wiederum eine Methode aufruft, die die Plugins lädt und die Informationen in der Ansicht anzeigt? Welche Ebene würde dann die Plugin-Referenzen steuern? Würde die Ansicht Verweise auf sie oder den Moderator enthalten?
Wenn die Plugins auf Ansichten bezogen sind, sollten die Ansichten über sie Bescheid wissen, nicht jedoch der Präsentator. Wenn es um Daten und Modelle geht, sollte die Ansicht nichts mit ihnen zu tun haben.
4. Habe ich Recht, wenn ich denke, dass die Ansicht alle Aspekte der Präsentation behandeln sollte, von der Farbe des Baumansichtsknotens bis zur Größe des Datagrids usw.?
Ja. Stellen Sie sich das als Präsentator vor, der XML bereitstellt, das Daten beschreibt, und die Ansicht, die die Daten aufnimmt und ein CSS-Stylesheet darauf anwendet. Konkret könnte der Moderator anrufen IRoadMapView.SetRoadCondition(RoadCondition.Slippery)
und die Ansicht zeigt die Straße dann in roter Farbe.
Was ist mit Daten für angeklickte Knoten?
5. Wenn ich beim Klicken auf die Treenodes den spezifischen Knoten an den Präsentator weiterleiten sollte, würde der Präsentator dann herausfinden, welche Daten er benötigt, und dann das Modell nach diesen Daten fragen, bevor er sie wieder der Ansicht präsentiert?
Wenn möglich, würde ich alle Daten, die erforderlich sind, um den Baum in einer Ansicht zu präsentieren, auf einmal übergeben. Aber wenn einige Daten zu groß sind, um von Anfang an weitergegeben zu werden, oder wenn sie von Natur aus dynamisch sind und den "neuesten Schnappschuss" des Modells (über den Präsentator) benötigen, würde ich event LoadNodeDetailsEventHandler LoadNodeDetails
der Ansichtsoberfläche so etwas hinzufügen , dass die Der Präsentator kann es abonnieren und die Details des Knotens LoadNodeDetailsEventArgs.Node
(möglicherweise über seine ID) aus dem Modell abrufen, damit die Ansicht die angezeigten Knotendetails aktualisieren kann, wenn der Event-Handler-Delegat zurückkehrt. Beachten Sie, dass möglicherweise asynchrone Muster erforderlich sind, wenn das Abrufen der Daten für eine gute Benutzererfahrung zu langsam ist.