Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MVVM Auto ViewModelLocator

0.00/5 (No votes)
23 May 2017 1  
Faster and easy way for generated ViewModel class

Introduction

I will present a little trick inside of the ViewModels instantiation 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 the ViewModels reference. We will not worry about feeding 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 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 is:

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, its 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 assembly {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 on a great project, or you need to 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here