Este post está dedicado a presentar un pequeño truco
referente a la instanciación automática de ViewModels
dentro del patrón MVVM.
En ocasiones, cuando trabajamos con pequeñas soluciones, no
necesitamos la presencia de la clase ViewModelLocator
como instanciador de clases ViewModels,
porque normalmente no necesitamos guardar ninguna referencia a ninguna de
ellas. En las siguientes líneas, aprenderemos a instanciar de forma automática
nuestras clases ViewModel, sin tener
que preocuparnos en registrarlas y crear propiedades de uso dentro de nuestro ViewModelLocator class.
Por supuesto, es completamente compatible con nuestras
pruebas unitarias.
Realizaremos todo este trabajo con una clase de apoyo WPF , que contendrá principalmente dos
attached properties:
Nos
encontraremos con 2 casos:
- El nombre de la vista y el nombre de la clase ViewModel, son equivalentes:
SameNameClass[View].xaml
- El nombre de la vista y el nombre de la clase ViewModel, NO son equivalentes:
DiferentNameView.xaml
DiferentNameViewModel.cs
Nombre Equivalentes (Vista y
ViewModel)
En este caso solo configuraremos una propiedad en la vista:
<Window x:Class="AutoMVVMLocator.Example1View" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AutoMVVMLocator" local:MLMVVM.IsAutomaticLocator="True" mc:Ignorable="d" Title="Example1View" Height="300" Width="300">
Parte importante:
local:MLMVVM.IsAutomaticLocator = “True”
En este ejemplo, la vista Example1View instancia un objeto de tipo Example1ViewModel como DataContext Automáticamente.
Nombres NO Equivalentes (Vista y
ViewModel)
En este caso, necesitaremos especificar una propiedad con el
nombre de la clase ViewModel.
<Window x:Class="AutoMVVMLocator.Example2Window" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AutoMVVMLocator" local:MLMVVM.ViewModelClassName="Example2ViewModel" local:MLMVVM.IsAutomaticLocator="True" mc:Ignorable="d" Title="Example2Window" Height="300" Width="396.546">
Parte importante:
local:MLMVVM.ViewModelClassName = “Example2ViewModel” local:MLMVVM.IsAutomaticLocator = “True”
Importante: la propiedad ViewModelClassName
tiene que estar definida en primer lugar.
In this
example, the view Example2Window
instances an Example2ViewModel
object as DataContext
automatically.
Clase MLMVVM
Esta clase muy simple, tiene dos AtachProperties y dos métodos privados.
Las AtachProperties, son las configuradas anteriormente:
- IsAutomaticLocator.- Activa la instanciación automática de clases ViewModels.
- ViewModelClassName.- Indica el nombre de la clase ViewModel a instanciar. Si el nombre de tu clase ViewModel es equivalente con el nombre de la vista, no es necesario configurar esta propiedad.
public static bool GetIsAutomaticLocator(DependencyObject obj) { return (bool)obj.GetValue(IsAutomaticLocatorProperty); } public static void SetIsAutomaticLocator(DependencyObject obj, bool value) { obj.SetValue(IsAutomaticLocatorProperty, value); } public static readonly DependencyProperty IsAutomaticLocatorProperty = DependencyProperty.RegisterAttached("IsAutomaticLocator", typeof(bool), typeof(MLMVVM), new PropertyMetadata(false, IsAutomaticLocatorChanged)); private static void IsAutomaticLocatorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var callOwner = d as FrameworkElement; var className = GetViewModelClassName(d); var userControl = GetInstanceOf(callOwner.GetType(), className); callOwner.DataContext = userControl; } public static string GetViewModelClassName(DependencyObject obj) { return (string)obj.GetValue(ViewModelClassNameProperty); } public static void SetViewModelClassName(DependencyObject obj, string value) { obj.SetValue(ViewModelClassNameProperty, value); } public static readonly DependencyProperty ViewModelClassNameProperty = DependencyProperty.RegisterAttached("ViewModelClassName", typeof(string), typeof(MLMVVM), new PropertyMetadata(null));
Los dos métodos privados contienen la mecánica de generación
dinámica de tipos ViewModels:
private static object GetInstanceOf(Type dependencyPropertyType, string className) { var assembly = dependencyPropertyType.Assembly; var assemblyTypes = assembly.GetTypes(); var classNameDef = GetClassName(dependencyPropertyType, className); var userControlType = assemblyTypes.FirstOrDefault(a => a.Name.Contains(classNameDef)); if (userControlType == null) throw new ArgumentException($"Not exist a type {classNameDef} in the asembly {assembly.FullName}"); var resultado = Activator.CreateInstance(userControlType); return resultado; } private static string GetClassName(Type dependencyPropertyType, string className) { if (string.IsNullOrWhiteSpace(className)) return $"{dependencyPropertyType.Name}Model"; return className; }
Limitaciones
Si trabajas en un gran proyecto, o necesitas guardar
referencias a los ViewModels intanciados, tú debes usar la clásica clase
ViewModelLocar y olvidarte de este intanciador automático, ya que tendrías que
realizar muchas virguerías para recuperar estas instancias.
Proyecto de Pruebas
En el Proyecto de pruebas, encontraremos código y ejemplos
para los dos casos.
Descargar de Aquí el proyecto de pruebas.
No hay comentarios :
Publicar un comentario