Intro
En MVVM, la práctica habitual es que las Vistas encuentren sus ViewModels resolviéndolos desde un contenedor de inyección de dependencia (DI). Esto sucede automáticamente cuando se le pide al contenedor que proporcione (resuelva) una instancia de la clase View. El contenedor inyecta ViewModel en la Vista llamando a un constructor de la Vista que acepta un parámetro ViewModel; este esquema se llama inversión de control (IoC).
Beneficios de DI
El principal beneficio aquí es que el contenedor se puede configurar en tiempo de ejecución con instrucciones sobre cómo resolver los tipos que le solicitamos. Esto permite una mayor capacidad de prueba indicándole que resuelva los tipos (Vistas y ViewModels) que usamos cuando nuestra aplicación realmente se ejecuta, pero instruyéndolo de manera diferente cuando ejecuta las pruebas unitarias para la aplicación. En el último caso, la aplicación ni siquiera tendrá una interfaz de usuario (no se está ejecutando, solo las pruebas), por lo que el contenedor resolverá las simulaciones en lugar de los tipos "normales" que se usan cuando se ejecuta la aplicación.
Problemas derivados de DI
Hasta ahora, hemos visto que el enfoque DI permite una fácil prueba de la aplicación al agregar una capa de abstracción sobre la creación de componentes de la aplicación. Hay un problema con este enfoque: no funciona bien con diseñadores visuales como Microsoft Expression Blend.
El problema es que tanto en las ejecuciones normales de la aplicación como en las pruebas unitarias, alguien tiene que configurar el contenedor con instrucciones sobre qué tipos resolver; además, alguien tiene que pedirle al contenedor que resuelva las Vistas para que los ViewModels puedan inyectarse en ellas.
Sin embargo, en tiempo de diseño no hay ningún código nuestro en ejecución . El diseñador intenta utilizar la reflexión para crear instancias de nuestras Vistas, lo que significa que:
- Si el constructor de vistas requiere una instancia de ViewModel, el diseñador no podrá crear una instancia de la vista en absoluto; se producirá un error de forma controlada.
- Si la vista tiene un constructor sin parámetros de la vista se crea una instancia, pero su
DataContext
será null
por lo que obtendrá una vista 'vacío' en el diseñador - que no es muy útil
Ingrese ViewModelLocator
ViewModelLocator es una abstracción adicional que se usa así:
- La propia vista crea una instancia de ViewModelLocator como parte de sus recursos y vincula su DataContext a la propiedad ViewModel del localizador
- El localizador detecta de alguna manera si estamos en modo diseño
- Si no está en modo de diseño, el localizador devuelve un ViewModel que resuelve desde el contenedor DI, como se explicó anteriormente.
- Si está en modo de diseño, el localizador devuelve un ViewModel "ficticio" fijo usando su propia lógica (recuerde: ¡no hay contenedor en tiempo de diseño!); este ViewModel normalmente viene rellenado previamente con datos ficticios
Por supuesto, esto significa que la Vista debe tener un constructor sin parámetros para empezar (de lo contrario, el diseñador no podrá instanciarlo).
Resumen
ViewModelLocator es un modismo que le permite mantener los beneficios de DI en su aplicación MVVM al mismo tiempo que permite que su código funcione bien con los diseñadores visuales. A esto a veces se le llama la "capacidad de mezcla" de su aplicación (refiriéndose a Expression Blend).
Después de digerir lo anterior, vea un ejemplo práctico aquí .
Por último, el uso de plantillas de datos no es una alternativa al uso de ViewModelLocator, sino una alternativa al uso de pares View / ViewModel explícitos para partes de su interfaz de usuario. A menudo, puede encontrar que no es necesario definir una vista para un modelo de vista porque puede usar una plantilla de datos en su lugar.
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
. El propósito del localizador es habilitar DI en las vistas, porque WPF es tan malo para proporcionarlo. Ejemplo: tiene una ventana principal que abre una ventana de diálogo. Para resolver la DI en la ventana de diálogo de la forma habitual, ¡necesitaría pasarla como una dependencia de la ventana principal! Esto se evita con el localizador de vistas.Una implementación de ejemplo de la respuesta de @ Jon
Tengo una clase de localizador de modelos de vista. Cada propiedad será una instancia del modelo de vista que voy a asignar a mi vista. Puedo comprobar si el código se está ejecutando en modo de diseño o no
DesignerProperties.GetIsInDesignMode
. Esto me permite usar un modelo simulado durante el diseño y el objeto real cuando ejecuto la aplicación.Y para usarlo puedo agregar mi localizador a los
App.xaml
recursos:Y luego para conectar su vista (por ejemplo: MainView.xaml) a su modelo de vista:
fuente
this
lugar dedummy
?No entiendo por qué las otras respuestas de esta pregunta giran en torno al Diseñador.
El propósito del Localizador de modelos de vista es permitir que su Vista cree una instancia de esto (sí, Localizador de modelos de vista = Ver primero):
en lugar de solo esto:
declarando esto:
Dónde
ViewModelLocator
está la clase, que hace referencia a un IoC y así es como resuelve laMainWindowModel
propiedad que expone.No tiene nada que ver con proporcionar modelos de vista simulada a su vista. Si quieres eso, hazlo
El Localizador de modelos de vista es un envoltorio de algún (cualquier) contenedor de Inversión de control, como Unity, por ejemplo.
Referirse a:
fuente