Esta es mi humilde opinión sobre MVP y sus problemas específicos.
Primero , cualquier cosa con la que un usuario pueda interactuar, o simplemente mostrarse, es una vista . Las leyes, el comportamiento y las características de tal vista se describen mediante una interfaz . Esa interfaz se puede implementar utilizando una interfaz de usuario de WinForms, una interfaz de usuario de consola, una interfaz de usuario web o incluso ninguna interfaz de usuario (generalmente cuando se prueba un presentador); la implementación concreta simplemente no importa siempre que obedezca las leyes de su interfaz de vista .
En segundo lugar , una vista siempre está controlada por un presentador . Las leyes, el comportamiento y las características de dicho presentador también se describen mediante una interfaz . Esa interfaz no tiene interés en la implementación de la vista concreta siempre que obedezca las leyes de su interfaz de vista.
En tercer lugar , dado que un presentador controla su vista, para minimizar las dependencias realmente no hay ganancia en que la vista sepa algo sobre su presentador. Existe un contrato acordado entre el presentador y la vista y eso lo establece la interfaz de la vista.
Las implicaciones de Third son:
- El presentador no tiene ningún método al que pueda llamar la vista, pero la vista tiene eventos a los que el presentador puede suscribirse.
- El presentador conoce su punto de vista. Prefiero lograr esto con inyección de constructor en el presentador de concreto.
- La vista no tiene idea de qué presentador la controla; simplemente nunca se le proporcionará un presentador.
Para su problema, lo anterior podría verse así en un código algo simplificado:
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()
{
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
{
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();
}
}
Además de lo anterior, normalmente tengo una IView
interfaz base en la que guardo la Show()
vista del propietario o el título de la vista de la que suelen beneficiarse mis vistas.
A sus preguntas:
1. Cuando se carga el winform, debe obtener una vista de árbol. ¿Estoy en lo cierto al pensar que la vista debería llamar a un método como: presenter.gettree (), esto a su vez delegará en el modelo, que obtendrá los datos para la vista de árbol, lo creará y lo configurará, lo devolverá al presentador, que a su vez pasará a la vista que luego simplemente lo asignará a, digamos, un panel?
Llamaría IConfigurationView.SetTreeData(...)
desde IConfigurationPresenter.ShowView()
, justo antes de la llamada aIConfigurationView.Show()
2. ¿Sería lo mismo para cualquier control de datos en Winform, ya que también tengo una vista de cuadrícula de datos?
Sí, lo llamaría IConfigurationView.SetTableData(...)
. Depende de la vista formatear los datos que se le proporcionen. El presentador simplemente obedece el contrato de la vista de que quiere datos tabulares.
3. Mi aplicación tiene varias clases de modelos con el mismo ensamblaje. También es compatible con una arquitectura de complementos con complementos que deben cargarse al inicio. ¿La vista simplemente llamaría a un método de presentador, que a su vez llamaría a un método que carga los complementos y muestra la información en la vista? Qué nivel controlaría las referencias de los complementos. ¿La vista tendría referencias a ellos o al presentador?
Si los complementos están relacionados con la vista, las vistas deberían conocerlos, pero no el presentador. Si se trata de datos y modelos, entonces la vista no debería tener nada que ver con ellos.
4. ¿Estoy en lo cierto al pensar que la vista debería manejar todo lo relacionado con la presentación, desde el color del nodo de la vista de árbol hasta el tamaño de la cuadrícula de datos, etc.?
Si. Piense en ello como el presentador que proporciona XML que describe los datos y la vista que toma los datos y les aplica una hoja de estilo CSS. En términos concretos, el presentador puede llamar IRoadMapView.SetRoadCondition(RoadCondition.Slippery)
y la vista luego muestra la carretera en color rojo.
¿Qué pasa con los datos de los nodos en los que se hace clic?
5. Si cuando hago clic en los árboles, ¿debo pasar a través del nodo específico al presentador y luego, a partir de ahí, el presentador calculará qué datos necesita y luego le pide al modelo esos datos, antes de presentarlos de nuevo a la vista?
Si es posible, pasaría todos los datos necesarios para presentar el árbol en una vista de una sola vez. Pero si algunos datos son demasiado grandes para pasarlos desde el principio o si son de naturaleza dinámica y necesitan la "última instantánea" del modelo (a través del presentador), entonces agregaría algo como event LoadNodeDetailsEventHandler LoadNodeDetails
a la interfaz de vista, para que el El presentador puede suscribirse a él, obtener los detalles del nodo LoadNodeDetailsEventArgs.Node
(posiblemente a través de su ID de algún tipo) del modelo, de modo que la vista pueda actualizar los detalles del nodo que se muestran cuando el delegado del controlador de eventos regrese. Tenga en cuenta que los patrones asíncronos de esto pueden ser necesarios si la obtención de los datos puede ser demasiado lenta para una buena experiencia de usuario.