Ejemplo # 1: Tengo una vista mostrada en mi aplicación MVVM (usemos Silverlight para los propósitos de la discusión) y hago clic en un botón que debería llevarme a una nueva página.
Ejemplo # 2: Esa misma vista tiene otro botón que, al hacer clic, debería abrir una vista de detalles en una ventana secundaria (diálogo).
Sabemos que habrá objetos de comando expuestos por nuestro ViewModel vinculados a los botones con métodos que responden al clic del usuario. Pero entonces, ¿qué? ¿Cómo completamos la acción? Incluso si usamos el llamado NavigationService, ¿qué le estamos diciendo?
Para ser más específicos, en un modelo tradicional de Ver primero (como los esquemas de navegación basados en URL, como en la web o el marco de navegación incorporado SL), los objetos de Comando tendrían que saber qué Vista mostrar a continuación. Eso parece cruzar la línea cuando se trata de la separación de preocupaciones promovidas por el patrón.
Por otro lado, si el botón no estaba conectado a un objeto Command y se comportaba como un hipervínculo, las reglas de navegación podrían definirse en el marcado. ¿Pero queremos que las Vistas controlen el flujo de la aplicación y que la navegación no sea solo otro tipo de lógica empresarial? (Puedo decir que sí en algunos casos y no en otros).
Para mí, la implementación utópica del patrón MVVM (y he oído que otros profesan esto) sería tener el ViewModel conectado de tal manera que la aplicación pueda ejecutarse sin cabeza (es decir, sin Vistas). Esto proporciona la mayor área de superficie para pruebas basadas en código y hace que las Vistas sean una verdadera máscara en la aplicación. Y mi ViewModel no debería importarme si se muestra en la ventana principal, un panel flotante o una ventana secundaria, ¿verdad?
De acuerdo con esta evaluación, depende de algún otro mecanismo en tiempo de ejecución 'vincular' qué Vista se debe mostrar para cada ViewModel. Pero, ¿qué sucede si queremos compartir una Vista con múltiples ViewModels o viceversa?
Entonces, dada la necesidad de administrar la relación View-ViewModel para saber qué mostrar cuando junto con la necesidad de navegar entre las vistas, incluida la visualización de ventanas / cuadros de diálogo secundarios, ¿cómo logramos realmente esto en el patrón MVVM?
fuente
DataTemplateSelector
, que es lo que generalmente uso para las aplicaciones de Silverlight. 2) He usado esto en situaciones de muchos a muchos antes. Por ejemplo, un ViewModel puede tener múltiples Vistas, o una Vista puede asociarse con múltiples ViewModels. Para ver un ejemplo de lo anterior, consulte rachel53461.wordpress.com/2011/05/28/… . Para lo posterior, solo necesita especificar que múltiples ViewModels se asignen a la misma vista en su DataTemplateSelector.CurrentPages
4) Una vez más, eso fue solo un ejemplo básico. En realidad, mis PageViewModels se basan en alguna clase base, por lo que mi ShellViewModel solo funciona con la clase base o interfaz, comoIPageViewModel
. Realmente la pieza de mapeo más desordenada es el DataTemplateSelector, que tendría que mapear 40 Vistas a 40 Modelos de Vista.En aras del cierre, pensé en publicar la dirección que finalmente elegí para resolver este problema.
La primera decisión fue aprovechar el marco de navegación de la página de Silverlight proporcionado de forma inmediata. Esta decisión se basó en varios factores, incluido el conocimiento de que Microsoft lleva este tipo de navegación a las aplicaciones de Metro de Windows 8 y es coherente con la navegación en las aplicaciones de Phone 7.
Para que funcione, luego analicé el trabajo que ASP.NET MVC ha realizado con la navegación basada en convenciones. El control Frame utiliza URI para ubicar la 'página' para mostrar. La similitud brindó la oportunidad de utilizar un enfoque similar basado en convenciones en la aplicación Silverlight. El truco fue hacer que todo funcionara de manera MVVM.
La solución es el NavigationService. Este servicio expone varios métodos, como NavigateTo y Back, que ViewModels puede usar para iniciar un cambio de página. Cuando se solicita una nueva página, NavigationService envía un mensaje CurrentPageChangedMessage utilizando la función MVVMLight Messenger.
La vista que contiene el control Frame tiene su propio ViewModel establecido como DataContext que está escuchando este mensaje. Cuando se recibe, el nombre de la nueva vista se transfiere a través de una función de mapeo que aplica nuestras reglas de convención y se establece en la propiedad CurrentPage. La propiedad Source del control Frame está vinculada a la propiedad CurrentPage. Como resultado, la configuración de la propiedad actualiza la Fuente y activa la navegación.
Volviendo al Servicio de navegación. El método NavigateTo acepta el nombre de la página de destino. Para asegurarse de que los ViewModels no tengan problemas de interfaz de usuario, el nombre utilizado es el nombre del ViewModel para mostrar. De hecho, creé una enumeración que tiene un campo para cada ViewModel navegable como ayuda y para eliminar cadenas mágicas en toda la aplicación. La función de mapeo que mencioné anteriormente eliminará el sufijo "ViewModel" del nombre, agregará "Página" al nombre y establecerá el nombre completo en "Vistas {Nombre} Page.xaml".
Entonces, por ejemplo, para navegar a la vista de detalles del cliente, puedo llamar a:
El valor de CustomerDetails es "CustomerDetailsViewModel", que se asigna a "Views \ CustomerDetailsPage.xaml".
La belleza de este enfoque es que la interfaz de usuario está completamente desacoplada de los ViewModels, pero tenemos soporte de navegación completo. Sin embargo, ahora puedo cambiar el tamaño de mi aplicación y siempre que lo crea conveniente sin ningún cambio de código.
Espero que la explicación ayude.
fuente
De manera similar a lo que dijo Rachel, mi aplicación MVVM en su mayoría tiene un
Presenter
control para cambiar entre ventanas o páginas. Rachel llama a esto unApplicationViewModel
, pero en mi experiencia, generalmente tiene que hacer más que ser un objetivo vinculante (como recibir mensajes, crear Windows, etc.), por lo que técnicamente es más como un tradicionalPresenter
oController
.En mi aplicación, mi
Presenter
comienza con aCurrentViewModel
. ElPresenter
intercepta toda comunicación entre elView
y elViewModel
. Una de las cosas queViewModel
puede hacer durante una interacción es devolver una nuevaViewModel
, lo que significa que seWindow
debe mostrar una nueva página o una nueva . ElPresenter
se encarga de crear o sobrescribirView
el nuevoViewModel
y configurar elDataContext
.El resultado de una acción también puede ser que a
ViewModel
esté "completo", en cuyo caso elPresenter
detecta esto y cierra la ventana, o saca estoViewModel
de la pila de VM y vuelve a mostrar la página anterior.fuente
Presenter
solo pega el devueltoViewModel
en el árbol visual, y WPF toma el apropiadoView
, lo conectaDataContext
y lo coloca en el árbol visual. Puede hacerlo utilizandoDataTemplate
s que declaran quéViewModel
tipo representan, o puede crear un selector de plantilla de datos personalizado. Eso todavía está dentro del ámbito de las características de WPF.