2011-05-06 8 views
16

Me he sentido desconcertado por esto por un tiempo. Estoy escribiendo una aplicación WPF RibbonWindow bastante grande usando el patrón MVVM. La pantalla tiene un menú RibbonBar en la parte superior y el resto muestra las distintas Vistas. Algunas Vistas contienen otras Vistas y algunas de ellas tienen botones que inician Windows.Al usar MVVM en WPF, ¿debo abrir ventanas secundarias desde el código de vista detrás, o ViewModel?

Hasta ahora, he estado haciendo esto desde el código de vista detrás del archivo, pero soy consciente de que se supone que estos archivos están vacíos cuando se usa MVVM. Podría mover el código de inicio de la ventana secundaria al ViewModel, pero luego necesitaría una referencia al RibbonWindow principal (para configurar como el propietario de la ventana secundaria) y eso no parece correcto.

Cualquier consejo o sugerencia sobre cómo esto se logra normalmente utilizando MVVM sería muy apreciado.

+4

No estoy de acuerdo con que el código de vista detrás de los archivos "debería estar vacío". Si bien hay muchas cosas que NO deberían estar detrás del código, todavía tienen valor. Lo uso para cualquier cosa que sea específica para la capa de Vista, como la administración del control de Enfoque. También lo uso para abrir ventanas posteriores en WPF. –

+1

+1 para Joel. La implementación MVVM significa que VER código ESPECÍFICO es el ÚNICO código que debería estar en el código subyacente. Su modelo de vista no debería tener que manejar cosas como cambios de estado de VSM (a menos que, tal vez, estos cambios sean impulsados ​​por datos, en cuyo caso lo incluiré en un comportamiento o disparador expuesto en el nivel VM) –

+0

Gracias por sus comentarios chicos. .. ellos realmente aclararon algunas cosas para mí. – Sheridan

Respuesta

19

Normalmente me encargo de esto creando algún tipo de WindowViewLoaderService. Cuando el programa inicializa se registra de su ventana y sus ViewModels con código de algo como esto:

WindowViewLoaderService.Register(TypeOf(MainWindowView), TypeOf(MainWindowViewModel); 
WindowViewLoaderService.Register(TypeOf(MyWindowView), TypeOf(MyWindowViewModel); 

A continuación, cuando se puede, por ejemplo, llamar a este servicio de su modelo de vista y todo lo que tiene que hacer referencia a otra es su modelo de vista. Por ejemplo, si usted está en su MainWindowViewModel es posible que tenga un código como éste:

var myChildWindowVM = new MyWindowViewModel(); 
WindowViewLoaderService.ShowWindow(myChildWindowVM) 

El WindowViewLoaderService sería entonces buscar lo que Vista está asociada con el modelo de vista determinado que ha pasado la misma. Creará esa Vista, establecerá su DataContext en el ViewModel que pasó, y luego mostrará la Vista.

De esta forma, su ViewModels nunca sabe acerca de ninguna vista.

Puede transferir uno de estos servicios con bastante facilidad. Todo lo que necesita hacer es mantener un Dictionary con la clave siendo su ViewModelType y el valor siendo su ViewType. El método Register se agrega a su diccionario y el método ShowWindow busca la vista correcta en función del ViewModel pasado, crea la vista, establece el DataContext y luego llama a Show en él.

La mayoría de los marcos de MVVM proporcionan algo como esto para ti de la caja. Por ejemplo, Caliburn tiene una elegante que solo usa la convención de nomenclatura, se llama ViewLocator en este Framework. Aquí hay un enlace que resume: http://devlicio.us/blogs/rob_eisenberg/archive/2010/07/04/mvvm-study-segue-introducing-caliburn-micro.aspx

la cincha en el otro lado lo llama un WPFUIVisualizerService que se puede ver en acción aquí: http://www.codeproject.com/KB/WPF/CinchIII.aspx

Estos deben ayudar a obtener rodando.

+1

+1 Gracias por su respuesta detallada. Aunque este enfoque me parece interesante, mi aplicación ya está bastante desarrollada. Las vistas están vinculadas a ViewModels utilizando 'DataTemplate'' Resources' y 'ContentControl's sin nombre que tienen sus propiedades' Content' vinculadas a las propiedades de ViewModel en otros ViewModels. Sería mucho trabajo cambiar esto, pero no está fuera de cuestión ... ¡si hubiera más tiempo en un día! – Sheridan

+0

Solo para que sepa que no es uno u otro enfoque por lo general. Casi siempre uso DataTemplates para hacer esto también. Sin embargo, como señaló, esto no funciona cuando necesita abrir una nueva ventana (o Diálogo para el caso). Cuando lo piensas, lo que estás haciendo con un DataTemplate es lo mismo que haces con un ViewLocator. Básicamente, "registras" un ViewModel en una Vista en función de su DataType ... de todos modos solo quería que supieras. Creo que lo estás haciendo correctamente. El único problema es cuando DataTemplates no se puede usar. –

+0

Gracias por ese comentario adicional, creo que definitivamente voy a investigar más sobre esto. – Sheridan

0

En esta situación, la vista debe manejar la apertura de las ventanas secundarias. Sin embargo, ViewModel podría conducir la creación de las ventanas, pero llamar a Ver para crear un nuevo Windows. Esto guardará la lógica del patrón MVVM: ViewModel tiene los "cerebros" pero no está involucrado en una creación de ventana particular.

+0

¿Pero cómo una Vista abre una nueva 'Ventana'? – Sheridan

5

Bueno, una observación para comenzar es que, "No tener ningún código en absoluto en el código subyacente" es en realidad un "mito". Si quieres ser pragmático, y ves que tener algún código (lo menos posible sería mejor) te facilitará la vida y resolverá tu problema, entonces deberías ir con eso.

Sin embargo, en esta situación, en realidad hay algunas formas poco flexibles de hacer esto. Podría tener un servicio que haga la interacción por usted.Usted inicia la interacción con el usuario desde ViewModel, el servicio se encarga de eso (al mostrar una ChildWindow por ejemplo) y le devuelve la respuesta del usuario. Ese servicio se puede burlar para probarlo fácilmente. Y puede ser probado por separado.

Es decir, si quiere hacer las cosas usted mismo. Si desea un marco para hacer el trabajo pesado para usted, puede consultar la función InteractionRequest ofrecida por Prisma. Aquí está el artículo de MSDN que habla sobre adanced MVVM scenarios que incluye una sección en User Interaction Patterns. Así es como lo hago, y es bastante simple, elegante y directo.

Espero que esto ayude :)

+1

+1 Gracias por sus comentarios y enlaces útiles. De hecho, investigué Prism hace un tiempo y descubrí que no necesitaba lo que ofrecía y decidí escribir un marco liviano personalizado en su lugar ... sin embargo, todavía estoy tratando de hacerlo bien. – Sheridan

+0

Me alegra ayudar. Buena suerte con su marco;) – AbdouMoumen

3

Para tomar de Matt respuesta un paso más allá, puede hacer que todos sean un control de usuario de su vista. Luego, cree un ViewContainer, que es una ventana con sus plantillas de datos (como describió).

Luego solo envía el modelo de vista que desea abrir al servicio de ventana, que establece DataContext. El servicio luego abriría la ventana y el control de contenido resolverá la vista correcta para el modelo de vista.

Esto significa que todo el registro se realiza en el XAML y el servicio de ventanas simplemente sabe cómo hacer eso ... abrir y cerrar ventanas.

+0

+1 Gracias por aclarar eso. – Sheridan

1

Esta es una publicación anterior, pero tal vez esto ayude a alguien en el camino: uso MVVM y planteo eventos para abrir ventanas secundarias desde ViewModel a la Vista. El único código detrás es manejar el evento, abrir la ventana, configurar el propietario de la ventana secundaria y eso es todo. En el modelo de vista, si el manejador de eventos es nulo, no está suscrito a la vista y no se dispara. La VM no sabe sobre la vista. El código es bastante simple también y solo toma unas pocas líneas.

0

ViewModel solo se usa para presentar el estado del sistema y la lógica de la IU. Un modelo de vista puede ser referenciado por múltiples vistas. No tiene conocimiento del código específico de UI como la relación padre/hijo, posición, diseño, tamaño, etc. Por lo tanto, es mejor abrir la ventana secundaria en el código subyacente de la vista con el evento de cambio de estado de ViewModel o eventos de comando y argumentos de evento. De esta forma, puede especificar cuál es la vista primaria en la capa UI.

Cuestiones relacionadas