Click here to Skip to main content
15,885,244 members
Articles / Desktop Programming / WPF
Tip/Trick

MVVM Auto ViewModelLocator

Rate me:
Please Sign up or sign in to vote.
4.22/5 (7 votes)
23 May 2017CPOL1 min read 11.9K   500   3  
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:

Image 1

SameNameClass[View].xaml

SameNameClass[ViewModel].cs

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

Image 2

DiferentNameView.xaml

DiferentNameViewModel.cs

Equivalents Names (View and ViewModel)

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

XML
<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:

XML
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.

XML
<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:

XML
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.
C#
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:

C#
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.

Image 3

Image 4

Image 5

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Cecabank
Spain Spain
MVP C# Corner 2017

MAP Microsoft Active Professional 2014

MCPD - Designing and Developing Windows Applications .NET Framework 4
MCTS - Windows Applications Development .NET Framework 4
MCTS - Accessing Data Development .NET Framework 4
MCTS - WCF Development .NET Framework 4

Comments and Discussions

 
-- There are no messages in this forum --