Model-View-Presenter en WinForms

Estoy intentando implementar el método MVP por primera vez, usando WinForms.

Estoy tratando de entender la función de cada capa.

En mi progtwig, tengo un botón GUI que, cuando se hace clic sobre él, abre una ventana de abrir archivos de registro.

Entonces, al usar MVP, la GUI maneja el evento de clic de botón y luego llama a presentater.openfile ();

Dentro de presentater.openfile (), ¿debería entonces delegar la apertura de ese archivo en la capa del modelo, o como no hay datos o lógica para procesar, simplemente debería actuar sobre la solicitud y abrir la ventana de abrirfiledialog?

Actualización: he decidido ofrecer una recompensa ya que creo que necesito más ayuda sobre esto, y preferiblemente adaptada a mis puntos específicos a continuación, para que tenga contexto.

De acuerdo, después de leer sobre MVP, he decidido implementar la vista pasiva. De hecho, tendré un montón de controles en un Winform que serán manejados por un Presentador y luego las tareas delegadas al Modelo (s). Mis puntos específicos están a continuación:

  1. Cuando se carga la forma de win, tiene que obtener una vista de árbol. ¿Estoy en lo cierto al pensar que la vista debería llamar a un método como: presentater.gettree (), que a su vez delegará en el modelo, que obtendrá los datos para la vista en árbol, lo creará y configurará, lo devolverá al presentador, que a su vez pasará a la vista que luego simplemente lo asignará a, digamos, un panel?

  2. ¿Sería esto lo mismo para cualquier control de datos en Winform, ya que también tengo una datagridview?

  3. Mi aplicación, tiene una serie de clases modelo con el mismo ensamblaje. También es compatible con una architecture de complemento con complementos que deben cargarse al inicio. ¿Llamaría la vista simplemente a un método 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 del complemento? ¿La vista tendría referencias a ellos o al presentador?

  4. ¿Estoy en lo cierto al pensar que la vista debe manejar cada cosa sobre la presentación, desde el color del nodo treeview, hasta el tamaño de la cuadrícula de datos, etc.?

Creo que son mis principales preocupaciones y si entiendo cómo debería ser el flujo de estos, creo que estaré bien.

Esta es mi humilde opinión sobre MVP y sus problemas específicos.

En primer lugar , todo lo que un usuario puede interactuar, o simplemente mostrar, es una vista . Las leyes, el comportamiento y las características de dicha vista se describen mediante una interfaz . Esa interfaz puede implementarse utilizando una interfaz de usuario WinForms, una interfaz de usuario de consola, una interfaz de usuario web o incluso sin interfaz de usuario (generalmente cuando se prueba un presentador): la implementación concreta no importa siempre que obedezca las leyes de su interfaz de visualización .

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 ningún interés en la implementación de la vista concreta, siempre que obedezca las leyes de su interfaz de visualización.

En tercer lugar , dado que un presentador controla su vista, para minimizar las dependencias realmente no hay ganancia al tener la vista sabiendo absolutamente nada sobre su presentador. Existe un contrato acordado entre el presentador y la vista, y así lo establece la interfaz de visualización.

Las implicaciones de Third son:

  • El presentador no tiene ningún método al que la vista pueda llamar, pero la vista tiene eventos a los que el presentador puede suscribirse.
  • El presentador conoce su punto de vista. Prefiero lograr esto con la inyección del constructor en el presentador concreto.
  • La vista no tiene idea de qué lo está controlando el presentador; nunca se le proporcionará ningún 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() { // 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(); 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, generalmente tengo una interfaz base de IView donde IView el Show() y cualquier vista de propietario o título de vista del que usualmente se benefician mis vistas.

A tus preguntas:

1. Cuando se carga la forma de win, tiene que obtener una vista de árbol. ¿Estoy en lo cierto al pensar que la vista debería llamar a un método como: presentater.gettree (), que a su vez delegará en el modelo, que obtendrá los datos para la vista en árbol, lo creará y configurará, lo devolverá al presentador, que a su vez pasará a la vista que luego simplemente lo asignará a, digamos, un panel?

IConfigurationView.SetTreeData(...) a IConfigurationView.SetTreeData(...) desde IConfigurationPresenter.ShowView() , justo antes de la llamada a IConfigurationView.Show()

2. ¿Sería esto lo mismo para cualquier control de datos en Winform, ya que también tengo una vista de malla de datos?

Sí, me gustaría llamar a IConfigurationView.SetTableData(...) para eso. Depende de la vista formatear los datos que se le dan. El presentador simplemente obedece el contrato de la vista que quiere datos tabulares.

3. Mi aplicación, tiene varias clases de modelo con el mismo ensamblaje. También es compatible con una architecture de complemento con complementos que deben cargarse al inicio. ¿Llamaría la vista simplemente a un método 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 del complemento? ¿La vista tendría referencias a ellos o al presentador?

Si los complementos están relacionados con la vista, las vistas deben conocerlos, pero no el presentador. Si se trata de datos y modelos, la vista no debería tener nada que ver con ellos.

4. ¿Estoy en lo cierto al pensar que la vista debe manejar cada cosa sobre la presentación, desde el color del nodo treeview, hasta el tamaño de la cuadrícula de datos, etc.?

Sí. Piénselo como el presentador que proporciona XML que describe los datos y la vista que toma los datos y le aplica una hoja de estilo CSS. En términos concretos, el presentador puede llamar a IRoadMapView.SetRoadCondition(RoadCondition.Slippery) y la vista luego muestra el camino en color rojo.

¿Qué pasa con los datos para los nodos cliqueados?

5. Si cuando hago clic en los treenodes, ¿debo pasar el nodo específico al presentador y luego de eso el presentador resolvería qué datos necesita y luego le preguntará al modelo por esos datos, antes de presentarlos nuevamente a la vista?

Si es posible, pasaría todos los datos necesarios para presentar el árbol en una vista de una vez. Pero si algunos datos son demasiado grandes para pasarlos desde el principio o si es dynamic en su naturaleza y necesita la “última instantánea” del modelo (a través del presentador), entonces agregaría algo como el event LoadNodeDetailsEventHandler LoadNodeDetails a la interfaz de visualización, para que el presentador pueda suscribirse, obtenga los detalles del nodo en LoadNodeDetailsEventArgs.Node (posiblemente a través de su ID de algún tipo) del modelo, de modo que la vista pueda actualizar los detalles de su nodo mostrado cuando el delegado del controlador de eventos regrese. Tenga en cuenta que los patrones asíncronos de esto pueden ser necesarios si recuperar los datos puede ser demasiado lento para una buena experiencia de usuario.

El presentador, que contiene toda la lógica en la vista, debe responder al botón al que se hace clic como dice @JochemKempe. En términos prácticos, el controlador de evento click click llama a presenter.OpenFile() . El presentador puede entonces determinar qué se debe hacer.

Si decide que el usuario debe seleccionar un archivo, vuelve a llamar a la vista (a través de una interfaz de visualización) y deja que la vista, que contiene todos los aspectos técnicos de la UI, muestre OpenFileDialog . Esta es una distinción muy importante en el sentido de que no se debe permitir que el presentador realice operaciones relacionadas con la tecnología UI en uso.

El archivo seleccionado se devolverá al presentador, que continúa su lógica. Esto puede implicar cualquier modelo o servicio que deba manejar el procesamiento del archivo.

La razón principal para usar un patrón MVP, imo es separar la tecnología UI de la lógica de vista. Por lo tanto, el presentador orquesta toda la lógica mientras que la vista la mantiene separada de la lógica de la interfaz de usuario. Esto tiene el efecto secundario muy agradable de hacer que el presentador sea totalmente comprobable por unidad.

Actualización: dado que el presentador es la encarnación de la lógica encontrada en una vista específica , la relación vista-presentador es IMO una relación uno-a-uno. Y para todos los fines prácticos, una instancia de vista (por ejemplo, un Formulario) interactúa con una instancia de presentador, y una instancia de presentador interactúa con una sola instancia de vista.

Dicho esto, en mi implementación de MVP con WinForms, el presentador siempre interactúa con la vista a través de una interfaz que representa las capacidades de la interfaz de usuario de la vista. No hay ninguna limitación sobre qué vista implementa esta interfaz, por lo tanto, diferentes “widgets” pueden implementar la misma interfaz de visualización y reutilizar la clase de presentador.

El presentador debe actuar en el extremo de la solicitud y mostrar la ventana de openfiledialog como sugirió. Como no se requieren datos del modelo, el presentador puede, y debe, manejar la solicitud.

Supongamos que necesita los datos para crear algunas entidades en su modelo. Puede pasar el canal de flujo a la capa de acceso donde tiene un método para crear entidades desde la secuencia, pero le sugiero que maneje el análisis del archivo en su presentador y use un constructor o método Create por entidad en su modelo.