Rasmus Tolstrup Christensen

Indexed ViewModelLocator With MVVMLight And Xamarin Forms

April 03, 2016 | 5 Minute Read

Working with MVVMLight toolkit and Xamarin Forms removes a lot of boiler plate coding. When it comes to the basic of resolving viewmodels this clean code gets a bit blurry, so let's see how we can improve this.

The default Viewmodel resolving

Building a solution with MVVMLight, the most common solution to resolve viewmodels is a mix of SimpleIOC and a ViewModelLocator. The ViewModelLocator provides basic binding for each ViewModel it exposes and Views consume the ViewModels by databinding.

<ContentPage
	xmlns="http://xamarin.com/schemas/2014/forms"
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	x:Class="BasicMvvmLight.Views.CarListView"
	BindingContext="{Binding CarVM, Source={StaticResource ViewModelLocator}}">
	<ContentPage.Content>
		<Label
			Text="{Binding Brand}">
		</Label>
	</ContentPage.Content>
</ContentPage>
public class ViewModelLocator
{
    public ViewModelLocator ()
    {
        ServiceLocator.SetLocatorProvider (() => SimpleIoc.Default);

        SimpleIoc.Default.Register<CarViewModel> ();		
    }
    
    [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Performance",
        "CA1822:MarkMembersAsStatic",
        Justification = "This non-static member is needed for data binding purposes.")]
    public CarViewModel CarVM {
        get { return ServiceLocator.Current.GetInstance<CarViewModel> (); }
}

The code above ensures that the BindingContext in the view is set by the CarVM binding where CarVM is the name of the binding in the ViewModelLocator providing the CarViewModel. As the solution grows, the ViewModelLocator becomes a class with a lot of basic getter’s just to resolve ViewModels, some pretty trivial code to write an maintain.

Indexed databinding

If we start by taking a look at the XAML code in the view, we currently have the Binding CarVM as a hard databinding to a specific property on the ViewModelLocator. Luckily we have the option to use an indexed binding, where we can specify a simple name as a string, we want to bind to and not a specific property. This means we can take advantage of a single index on the viewmodel to resolve viewmodels and less code to maintain! The flow is illustrated below.

viewmodellocatorflow

Refactor View

The first thing we need to do is change the binding in the view to make use of the indexer. As you can see I’ve also changed the name of the index to CarViewModel. this is because we want to use the binding index as type lookup in the ViewModelLocator later meaning the name should match a corresponding type.

<ContentPage
	xmlns="http://xamarin.com/schemas/2014/forms"
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	x:Class="BasicMvvmLight.Views.CarListView"
	BindingContext="{Binding [CarViewModel], Source={StaticResource ViewModelLocator}}">
	<ContentPage.Content>
		<Label
			Text="{Binding Brand}">
		</Label>
	</ContentPage.Content>
</ContentPage>

Refactor ViewModelLocator

The refactor of our ViewModelLocator introduces some new concepts. As specified by the view, we should provide an indexer. The indexer is added by letting the ViewModelLocator inherit from DynamicObject. This makes TryGetMember available, which the binding in the view will target. Combined with this, we add an indexer which use the final piece of the puzzle, the ViewModelResolver.

public class ViewModelLocator : DynamicObject
{
    static ViewModelResolver _resolver;

    public ViewModelLocator ()
    {
        ServiceLocator.SetLocatorProvider (() => SimpleIoc.Default);

        SimpleIoc.Default.Register<CarViewModel> ();
    }

    public static ViewModelResolver Resolver {
        get {
            if (_resolver == null) {
                _resolver = new ViewModelResolver ();
            }
            return _resolver;
        }
    }

    public object this [string viewModelName] {
        get {               
            return Resolver.Resolve (viewModelName);
        }
    }

    public override bool TryGetMember (GetMemberBinder binder, out object result)
    {
        result = this [binder.Name];
        return true;
    }
}

Introducing ViewModelResolver

The responsibility of the ViewModelResolver is to resolve an instance of a ViewModel, from a name specifying its type. One thing to keep in mind, the ViewModelResolver is located inside a PCL. This is necessary to know when we resolve the type from the assembly. We find all the types defined and make a match on the provided name. When located, the instance is resolved and all dependencies it might might require (constructor based) will also be resolved, when using the DI Container.

public class ViewModelResolver
{		
    public ViewModelResolver ()
    {}       

    public object Resolve (string viewModelName)
    {
        var vmtype = this.GetType ()
            .GetTypeInfo ()
            .Assembly
            .DefinedTypes
            .FirstOrDefault (t => t.Name.Equals (viewModelName))
            .AsType ();

        return ServiceLocator.Current.GetInstance (vmtype);
    }
}

With the ViewModelResolver in place we can now run the project and if we extend the app with more view and viewmodels, we just need to ensure everything is registered in the DI container. If another DI container like Autofac is used instead, it can simply be injected into the ViewModelResolver.

The complete source code is available here GitHub Repository.

comments powered by Disqus