2010-11-14 9 views
12

Estoy comenzando mi primera incursión en el mundo de Prism v4/MVVM con MEF & WPF. He creado con éxito un shell y, utilizando MEF, puedo descubrir e inicializar módulos. Sin embargo, no estoy seguro de la forma correcta de proporcionar navegación a las vistas expuestas por estos módulos.Prisma/MVVM (MEF/WPF): Exposición de navegación [Menú por ejemplo] de los módulos

Por ejemplo, supongamos que uno de los módulos expone tres vistas y quiero mostrar la navegación a estas vistas en un control de menú. Hasta ahora, he expuesto una vista correctamente en base a un MenuItem y este MenuItem contiene controles secundarios MenuItem proporcionando así una jerarquía de comandos que se puede usar. Estupendo.

La cosa es, esto se siente mal. Ahora estoy afirmando dentro de mi módulo que la navegación (y por lo tanto el shell) DEBE soportar el uso de los menús. ¿Qué sucede si quiero cambiar a usar un ToolBar o incluso un Ribbon? Entonces tendría que cambiar todos mis módulos para exponer los tipos de control correspondientes para el shell.

He visto alrededor y en algunos sitios se menciona el uso de un "Servicio" para proporcionar navegación, por lo que durante la inicialización del módulo, las opciones de navegación se agregan al servicio que a su vez es utilizado por el shell para mostrar esta navegación en el formato que desee (ToolBar, TreeView, Ribbon, MenuItem etc.) - pero no puedo encontrar ningún ejemplo de esto.

Para poner todo esto en perspectiva, finalmente estoy buscando poder seleccionar vistas desde un menú y/u otro control de navegación (probablemente un Ribbon) y luego abrir esas vistas bajo demanda dentro de un TabControl. Ya he llegado a poder crear las vistas en el TabControl en el momento de la inicialización del módulo, ahora necesito el siguiente paso.

Lo que necesito saber es esto: cuál sería la forma correcta de exponer las opciones de navegación de forma tal que no se insista en el soporte de un control específico por parte del shell, y si un servicio es el camino a seguir, entonces ¿Cómo se podría juntar esto dentro de los patrones Prism/MVVM?

Gracias de antemano por cualquier información que pueda ofrecer.

Respuesta

7

Supongo que tiene un módulo principal que contiene interfaces comunes. Se puede crear una interfaz sencilla como

public interface IMenuService { 
    void AddItem(string name, Action action); 
    IEnumerable<MenuItemViewModel> GetItems { get; } 
} 

Crear 1 ejecución y una sola instancia.

public class MenuService : IMenuService { 

    private readonly IList<MenuItemViewModel> items = new List<MenuItemViewModel>(); 

    void AddItem(string name, Action action) { 
     items.Add(new MenuItemViewModel { 
      Name = name, 
      Action = action 
     }); 
    } 

    IEnumerable<MenuItemViewModel> GetItems { 
     get { return list.AsEnumerable(); } 
    } 
} 

Dentro de sus módulos, utilice MEF para resolver este caso y llamar AddItem() dar de alta sus puntos de vista. La propiedad Action es un simple delegado para activar una vista o hacer cualquier otra cosa.

Luego, en su caparazón o en cualquier vista, solo necesita llamar a la propiedad GetItems para completar su menú.

+0

Me gusta esto, ya que es una opción totalmente genérica que deja el caparazón para decidir cómo va a mostrar los elementos. Dicho esto, he tenido más ideas propias sobre el tema y realmente he ido por otra ruta que documentaré como una respuesta separada. Gracias. –

+0

No olvide que puede hacer que un 'MenuItemViewModel' se ajuste a MenuItems (verificable o que tenga subelementos). Tendrás que hacer una interfaz más específica. Pero este fue un ejemplo. Que te diviertas. – SandRock

1

Después de haber pensado un poco más sobre esto, he llegado a la siguiente conclusión de que siento que afecta la forma en que debo lidiar con esto ...

Los módulos deben ser parcialmente conscientes del diseño de la carcasa de todos modos, es decir, la carcasa expone una cantidad de regiones y los módulos deben conocer esas regiones (por nombre y lo que se espera mostrar) para llenarlos correctamente cuando se solicita la funcionalidad (ya sea mediante el registro de una vista dentro de una región o como reacción a una acción del usuario).

Debido a esto, los módulos deben diseñarse para interactuar con el shell para colocar contenido en las regiones nombradas y, como tal, no veo ninguna razón para que los módulos no expongan cualquier tipo de navegación que admita el shell.

Por lo tanto, mis módulos (actualmente) exponen un "RibbonView" (un RibbonTab) con los iconos, botones y comandos necesarios, etc. para exponer la funcionalidad del módulo. Cada "RibbonView" se registra con la "RibbonRegion" del intérprete de comandos, junto con sugerencias para ordenar, y esto se representa dentro del intérprete de órdenes.

Si en el futuro decido actualizar mi concha utilizar la última + control de la navegación mayor (sea lo que sea en x años de tiempo), entonces simplemente necesito para actualizar cada uno de los módulos para exponer los elementos necesarios para integrar con esa nueva navegación y, como estoy cargando en un nuevo shell, puedo actualizar el registro de mi vista en consecuencia.

Espero que no rompa ninguno de los principios de la aplicación compuesta al hacer esto, pero dicho esto, nunca he encontrado un patrón que realmente pueda implementarse en un escenario real sin alguna "interpretación".

Me gustaría saber si alguien tiene alguna opinión al respecto.

0

He encontrado la misma situación, y creo que la solución está en diferenciar entre la interfaz y la implementación. Por ejemplo, puede diseñar una vista en un módulo que realiza una función determinada. Eso es todo lo que hace. Tan pronto como use o consuma esto en un contexto específico, habrá pasado a la implementación. Ahora, idealmente, la vista no tiene conocimiento de cómo se está implementando, y ciertamente no tendría conocimiento de las Regiones nombradas en el Shell. Entonces, las vistas de asientos en Regions dentro de un módulo es un no-no.

Para evitar esto, he optado por delegar esta responsabilidad en un componente de terceros, un LayoutManager. LayoutManager se ubica entre Shell y Module y define "what goes where". Es una implementación específica, y realmente define la implementación. Tanto el Shell como la vista del Módulo siguen siendo genéricos.

Tenga una mirada en: http://rgramann.blogspot.com/2009/08/layout-manager-for-prism-v2.html

que le puede dar algunas ideas en torno a este problema.

Espero que ayude.

+0

Estoy totalmente de acuerdo en que conociendo las regiones de caparazón mis módulos se vinculan a una implementación específica, pero hasta ahora estaba luchando por ideas sobre cómo eludir esto. Aunque su artículo no aborda directamente mi situación (usted solo tiene una región y usted está al tanto de las vistas que se están cargando para ponerlas en su configuración), me ha dado algunas ideas sobre cómo podría solucionar el problema. problema. Gracias. –

0

article utiliza una abstracción (IMenuItem) para representar ViewModels para sus opciones de menú. La forma en que realmente renderiza estos objetos importados depende realmente de la aplicación host. El ejemplo usa un menú de WPF, pero sin duda podría renderizarlo de la forma que quisiera porque IMenuItem es lo suficientemente abstracto.

Si cambiaste IMenuItem a INavigationItem, tienes lo que quieres.

En ese artículo, cuando el elemento de navegación particular recibe una notificación de que se ha "ejecutado", generalmente instancia un ViewModel para un documento o "pad", y lo pasa al servicio ILayoutManager.Tiene una arquitectura conectable, por lo que puede cambiar el servicio de LayoutManager por un motor de diseño diferente (el predeterminado es un contenedor alrededor de AvalonDock).

Cuestiones relacionadas