MVVM Auto ViewModelLocator

0
29

Introduction

I will present a little trick inside of the ViewModels instantation in MVVM pattern.

Sometimes, we work in a WPF small solutions in which isn’t usually needed the ViewModelLocator class as ViewModels class generator, because we don’t need save de ViewModels reference. We will not worry to feed the ViewModelLocator class and we will go to deploy our ViewModels classes immediately. This fits perfectly with the unit tests and binding engine.

Background

We will have two cases:

  • The View name and the ViewModel name have a equivalents names:

                SameNameClass[View].xaml

                SameNameClass[ViewModel].cs

  • The View name and the ViewModel name don’t have a equivalents names:

                DiferentNameView.xaml

                DiferentNameViewModel.cs   

Equivalents Names (View and ViewModel)

In this case, only config a one property in the view:

<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">

The important part:

local:MLMVVM.IsAutomaticLocator = "True"

In this example, the view Example1View instances an Example1ViewModel object as DataContext automatically.

Not Equivalents Names (View and ViewModel)

In this case, we need specify another property with the name of ViewModel class.

<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">

The important part:

local:MLMVVM.ViewModelClassName = "Example2ViewModel" local:MLMVVM.IsAutomaticLocator = "True"

Note: The property ViewModelClassName has to be in first place.

In this example, the view Example2Window instances an Example2ViewModel object as DataContext automatically.

MLMVVM class

It is a very simple class. It has two AtachProperties and two private methods.

The attachproperties are the previous configured properties:

  • IsAutomaticLocator.- Activated the automatic instance ViewModels classes.
  • ViewModelClassName.- Show the ViewModel class name to instance. If your ViewModelClass name is equivalent with the View name, it property isn’t necessary.
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));

The two private’s methods contains a dynamic instance engine:

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; }

Limitations

If you work in a great project, or you need recover the references of instances ViewModels, you must use a classical ViewModelLocator class.

Test Project

In the test project, we have the code and example for all cases.

LEAVE A REPLY