Pregunta

Estoy diseñando una interfaz gráfica de usuario que tiene la siguiente idea básica (modelada de manera similar después de look-and-feel básico de Visual Studio):

  1. Archivo de navegación
  2. selector de control (para seleccionar lo que desea mostrar en el componente de edición)
  3. Editor
  4. Logger (errores, advertencias, confirmación, etc.)

Por ahora, voy a utilizar un TreeView para la navegación de archivos, un ListView para la selección de los controles que se mostrará en el editor y un RichTextBox para el registrador. El Editor tendrá 2 tipos de modos de edición dependiendo de lo que esté seleccionado en el árbol. El Editor será ya sea un RichTextBox para editar manualmente los archivos de texto dentro, o será un panel con Drag / Drop DataGridViews y sub-cuadros de texto para la edición de este panel.

Estoy tratando de seguir el patrón de diseño pasivo Vista para la separación completa del modelo de Vista y viceversa. La naturaleza de este proyecto es que cualquiera que añadir el componente está sujeto a editar / extracción. Como tal, no necesito a la independencia de un control dado a la siguiente. Si hoy estoy usando un TreeView para la navegación de archivos, pero mañana me han dicho que usar algo más, entonces yo quiero poner en práctica un nuevo control con relativa facilidad.

Simplemente no entiendo cómo estructurar el programa. Entiendo un presentador por control, pero no sé cómo hacer que funcione de tal manera que tengo una visión (toda la interfaz gráfica de usuario del programa) con los controles (sub-Visto) de tal manera que todo el Vista es reemplazable, así como el individuo controles que reflejan mi modelo.

En la vista principal, que se supone que es de peso ligero por pasiva Ver normas, puedo implementar las sub-Vistas de forma individual? Si es así, decir que tengo una inavigator interfaz para abstraer el papel del navegador de objetos. El navegador se necesita un presentador y un modelo para actuar entre la vista del navegador y la vista principal. Siento que me estoy perdido en algún lugar del patrón de diseño jerga.

La pregunta más relacionada de manera similar se puede encontrar aquí , pero no lo hace responder a mi pregunta con suficiente detalle.

Will alguien por favor me ayude a entender cómo la "estructura" de este programa? Agradezco cualquier ayuda.

Gracias,

Daniel

¿Fue útil?

Solución

La abstracción es buena, pero es importante recordar que en algún momento algo tiene que saber una cosa o dos acerca de una cosa o dos, o de lo contrario sólo tendremos un montón de legos muy bien resumieron sentado en el suelo en lugar de ellos siendo ensamblado en una casa.

Una inversión de control / inyección de dependencias / abajo flippy-dippy-invertida lo-estamos-llamar-que-esta-semana contenedor como autofac puede realmente ayuda en juntar las piezas todo esto junto.

Cuando lanzo juntos una aplicación WinForms, por lo general terminan con un patrón de repetición.

Voy a empezar con un archivo Program.cs que configura el contenedor Autofac y luego obtiene una instancia de la MainForm de ella, y muestra la MainForm. Algunas personas llaman a esto la carcasa o el espacio de trabajo o en el escritorio, pero en todo caso se trata de "forma" que tiene la barra de menús y pantallas de cualquiera de los niños ventanas o controles de usuario del niño, y cuando se cierra, se cierra la aplicación.

El siguiente es el MainForm antes mencionado. Puedo hacer las cosas básicas como arrastrar y dejar caer un poco SplitContainers y MenuBars y tal en el diseñador de Visual Studio Visual, y luego empezar a recibir de lujo en código: Voy a tener ciertas interfaces clave "inyecta" en el constructor de la MainForm para que yo puede hacer uso de ellos, por lo que mi MainForm puede organizar controles secundarios sin realmente tener que saber mucho acerca de ellos.

Por ejemplo, yo podría tener una interfaz IEventBroker que permite a los diversos componentes publicar o suscribirse a "eventos" como BarcodeScanned o ProductSaved. Esto permite que partes de la aplicación para responder a eventos de una manera imprecisa, sin tener que confiar en el cableado de los eventos tradicionales de .NET. Por ejemplo, el EditProductPresenter que va junto con mi EditProductUserControl podría decir this.eventBroker.Fire("ProductSaved", new EventArgs<Product>(blah)) y la IEventBroker comprobaría su lista de suscriptores para ese evento y llamar a sus devoluciones de llamada. Por ejemplo, el ListProductsPresenter podría escuchar para ese evento y actualizar dinámicamente el ListProductsUserControl que está conectada. El resultado neto es que si un usuario guarda un producto en un control de usuario, presentadora de otro control de usuario puede reaccionar y actualizarse si le pasa a estar abierto, sin que ninguno de control que tiene que ser consciente de la existencia del otro, y sin la MainForm tener que orquestar ese evento.

Si estoy diseñando una aplicación MDI, que podría tener la MainForm implementar una interfaz que tiene IWindowWorkspace métodos Open() y Close(). Podría inyectar esa interfaz en mis varios presentadores que les permita ventanas adicionales se abren y cierran sin que sean conscientes de la MainForm directamente. Por ejemplo, el ListProductsPresenter podría querer abrir una EditProductPresenter y correspondiente EditProductUserControl cuando el usuario hace doble clic en una fila en una cuadrícula de datos en un ListProductsUserControl. Se puede hacer referencia a un IWindowWorkspace - que en realidad es la MainForm, pero no tiene por qué saber que - y Open(newInstanceOfAnEditControl) llamada y asumir que el control se muestra en el lugar apropiado de la aplicación de alguna manera. (La aplicación MainForm sería, presumiblemente, cambiar el control a la vista en algún lugar en un panel.)

Pero, ¿cómo diablos la ListProductsPresenter crear esa instancia de la EditProductUserControl? fábricas delegado de autofac son una verdadera alegría aquí, ya que sólo puede inyectar un delegado en el presentador y Autofac automágicamente cablear hacia arriba como si se tratara de una fábrica (pseudocódigo siguiente):


public class EditProductUserControl : UserControl
{
    public EditProductUserControl(EditProductPresenter presenter)
    {
        // initialize databindings based on properties of the presenter
    }
}

public class EditProductPresenter
{
    // Autofac will do some magic when it sees this injected anywhere
    public delegate EditProductPresenter Factory(int productId);

    public EditProductPresenter(
        ISession session, // The NHibernate session reference
        IEventBroker eventBroker,
        int productId)    // An optional product identifier
    {
        // do stuff....
    }

    public void Save()
    {
        // do stuff...
        this.eventBroker.Publish("ProductSaved", new EventArgs(this.product));
    }
}

public class ListProductsPresenter
{
    private IEventBroker eventBroker;
    private EditProductsPresenter.Factory factory;
    private IWindowWorkspace workspace;

    public ListProductsPresenter(
        IEventBroker eventBroker,
        EditProductsPresenter.Factory factory,
        IWindowWorkspace workspace)
    {
       this.eventBroker = eventBroker;
       this.factory = factory;
       this.workspace = workspace;

       this.eventBroker.Subscribe("ProductSaved", this.WhenProductSaved);
    }

    public void WhenDataGridRowDoubleClicked(int productId)
    {
       var editPresenter = this.factory(productId);
       var editControl = new EditProductUserControl(editPresenter);
       this.workspace.Open(editControl);
    }

    public void WhenProductSaved(object sender, EventArgs e)
    {
       // refresh the data grid, etc.
    }
}

Así que la ListProductsPresenter sabe sobre el conjunto de características Edit (es decir, la edición presentador y el control de edición del usuario) - unand esto está perfectamente bien, van mano a mano - pero no necesita saber acerca de todos los dependencias del conjunto de características Edit, en lugar de confiar en un delegado proporcionada por Autofac a resolver todas esas dependencias para ello.

En general, me parece que tengo un uno-a-uno entre un "presentador / vista del modelo / controlador de supervisión" (vamos no demasiado atrapados en las diferencias que al final del día todos ellos son bastante similares) y un "UserControl / Form". El UserControl acepta el presentador / vista de modelo / controlador en su constructor y databinds como es apropiado, difiriendo al presentador tanto como sea posible. Algunas personas ocultan la UserControl del presentador a través de una interfaz, como IEditProductView, lo cual puede ser útil si la vista no es completamente pasivo. Que tienden a utilizar enlace de datos para todo, por lo que la comunicación se realiza a través de INotifyPropertyChanged y no se molestan.

Sin embargo, se le hará la vida mucho más fácil si el presentador está sin vergüenza ligada a la vista. ¿Tiene una propiedad en su modelo de objetos no concuerda con enlace de datos? Exponer una nueva propiedad por lo que hace. Usted nunca va a tener un EditProductPresenter y un EditProductUserControl con un diseño y luego desea escribir una nueva versión del control de usuario que trabaja con el mismo presentador. Usted sólo editar los dos, que son para todos los intentos y una unidad de propósito, una característica, el presentador única existente ya que es fácilmente comprobable unidad y el control de usuario no lo es.

Si desea una característica que sea reemplazable, lo que necesita para abstraer toda la función como tal. Lo que podría tener una interfaz INavigationFeature que sus conversaciones MainForm a. Puede tener un TreeBasedNavigationPresenter que implementa INavigationFeature y es consumido por un TreeBasedUserControl. Y es posible que tenga un CarouselBasedNavigationPresenter que también implementa INavigationFeature y es consumido por un CarouselBasedUserControl. Los controles de usuario y los presentadores todavía van mano a mano, pero su MainForm no tendrían que preocuparse si está interactuando con una visión basada en árbol o un ser basado en un carrusel, y que podría cambiarlos sin la MainForm ser el más prudente.

Para terminar, es fácil confundir a ti mismo. Todo el mundo es pedante y utiliza una terminología ligeramente diferente para transmitir que sutiles (y muchas veces sin importancia) las diferencias entre lo que son los patrones arquitectónicos similares. En mi humilde opinión, la inyección de dependencia hace maravillas para la creación de aplicaciones componibles, extensibles, ya que se mantiene el acoplamiento abajo; separación de funciones en "presentadores / modelos Ver / controladores" y "vistas / controles de usuario / formularios" hace maravillas para la calidad ya que la mayoría lógica se tira en la primera, permitiendo que sea fácilmente unidad de prueba; y la combinación de los dos principios parece ser realmente lo que está buscando, usted está confundiendo en la terminología.

O, podría estar lleno de ella. Buena suerte!

Otros consejos

Sé que esta pregunta es casi 2 años de edad, pero me encuentro en una situación muy similar. Al igual que usted, he recorrido el Internet para los días y que no se encuentra un ejemplo concreto que se adapte a mis necesidades - cuanto más buscado más me sigo viniendo a los mismos sitios una y otra vez hasta el punto en que tenía sobre 10 páginas de color púrpura enlaces en Google!

De todos modos, me preguntaba si alguna vez se le ocurrió una solución satisfactoria al problema? Voy a esbozar cómo he ido sobre él hasta ahora, basado en lo que he leído en la última semana:

Mis objetivos eran: forma pasiva, presentadora primero (el presentador crea una instancia del formulario para el formulario no tiene conocimiento de que la presentadora) Llamar a métodos en el presentador elevando eventos en la forma (vista)

La aplicación tiene una única FormMain que contiene 2 controles de usuario:

ControlsView (tiene 3 botones) DocumentView (Una imagen de 3 ª parte del Visor de miniaturas)

El "formulario principal" tiene una barra de herramientas para el archivo de costumbre guardar cosas, etc., y poco más. El control de usuario "ControlsView" permite al usuario hacer clic en "Escanear documentos" También contiene un control de vista de árbol para mostrar una jerarquía de documentos y páginas Los "DocumentView" muestra imágenes en miniatura de los documentos escaneados

Realmente me sentí que cada control debe tener su propia tríada MVP, así como la forma principal, pero quería que todos ellos hacen referencia al mismo modelo. Simplemente no podía encontrar la manera de coordinar la comunicación entre los controles.

Por ejemplo, cuando el usuario hace clic en "Scan", la ControlsPresenter se encarga de la adquisición de las imágenes desde el escáner y yo quería que añadir la página a la vista de árbol, ya que cada página devuelta desde el escáner - no hay problema - pero también querían que la imagen para aparecer en el DocumentsView al mismo tiempo (problema, ya que los presentadores no saben el uno del otro).

Mi solución fue para el ControlsPresenter para llamar a un método en el modelo de añadir la nueva página en el objeto de negocio, y luego en el modelo levanto un evento "PageAdded".

Entonces tiene tanto la ControlsPresenter y la "escucha" DocumentPresenter a este evento para que la ControlsPesenter dice que es objeto de añadir la nueva página a la vista de árbol, y el DocumentPresenter dice que es objeto de añadir la nueva imagen en miniatura.

Para resumir:

controles de vista - plantea evento "ScanButtonClicked"

Controles Presentador - oye el evento, llama a la clase escáner a AcquireImages de la siguiente manera:

GDPictureScanning scanner = new GDPictureScanning();

IEnumerable<Page> pages = scanner.AquireImages();
foreach (Page page in pages)
{
m_DocumentModel.AddPage(page);                
//The view gets notified of new pages via events raised by the model
//The events are subscribed to by the various presenters so they can 
//update views accordingly                
}

A medida que se escanea cada página, el circuito de barrido llama un "retorno rendimiento nueva página (PageID)". El método anterior llama m_DocumentModel.AddPage (página). La nueva página se añade al modelo, lo que provoca un evento. Ambos controles presentador y presentadora documento "escuchar" el evento y añadir elementos en consecuencia.

El bit no estoy "seguro" es de la inicialización de todos los presentadores - que estoy haciendo esto dentro de Program.cs de la siguiente manera:

static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

IDocumotiveCaptureView view = new DocumotiveCaptureView();
IDocumentModel model = new DocumentModel();
IDocumotiveCapturePresenter Presenter = new DocumotiveCapturePresenter(view, model);
IControlsPresenter ControlsPresenter = new ControlsPresenter(view.ControlsView, model);
IDocumentPresenter DocumentPresenter = new DocumentPresenter(view.DocumentView, model);

Application.Run((Form)view);                                                         
}

No estoy seguro si esto es bueno, malo o! Indiferente

De todos modos, lo que es un gran post sobre una vieja la pregunta dos años - sea bueno para obtener alguna información, aunque ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top