How to integrate unity container with modern UI

Dec 3, 2013 at 6:42 PM
Hi

I would like to use unity container with WPF modren UI.

Any help is appreciated. Thanks a lot.
Dec 5, 2013 at 6:51 AM
Edited Dec 5, 2013 at 7:04 AM
Yeah, I had to do the similar, MEF gave me grief, when I wanted to have more complicated management of metadata. I attribute that to a lack of knowledge of MEF on my side.

A few things to get Unity DI, which I'm sure you already know. Pull through the nugget packages for Unity and ServiceLocator.

Step 1) Create a UnityServiceLocator.cs
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
/// <summary>
/// Unity Service Locator
/// </summary>
public class UnityServiceLocator : IServiceLocator
{
    private readonly IUnityContainer _unityContainer;
    /// <summary>
    /// Initializes a new instance of the <see cref="UnityServiceLocator"/> class.
    /// </summary>
    /// <param name="unityContainer">The unity container.</param>
    public UnityServiceLocator(IUnityContainer unityContainer)
    {
        _unityContainer = unityContainer;
    }
    /// <summary>
    /// Gets all instances.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <returns></returns>
    public IEnumerable<TService> GetAllInstances<TService>()
    {
        return _unityContainer.ResolveAll<TService>();
    }

    /// <summary>
    /// Get all instances of the given <paramref name="serviceType" /> currently
    /// registered in the container.
    /// </summary>
    /// <param name="serviceType">Type of object requested.</param>
    /// <returns>
    /// A sequence of instances of the requested <paramref name="serviceType" />.
    /// </returns>
    public IEnumerable<object> GetAllInstances(Type serviceType)
    {
        foreach (object instance in _unityContainer.ResolveAll(serviceType))
        {
            yield return instance;
        }
        //return _unityContainer.ResolveAll(serviceType);
    }

    /// <summary>
    /// Gets the instance.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public TService GetInstance<TService>(string key)
    {
        return _unityContainer.Resolve<TService>(key);
    }

    /// <summary>
    /// Gets the instance.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <returns></returns>
    public TService GetInstance<TService>()
    {
        return _unityContainer.Resolve<TService>();
    }

    /// <summary>
    /// Get an instance of the given named <paramref name="serviceType" />.
    /// </summary>
    /// <param name="serviceType">Type of object requested.</param>
    /// <param name="key">Name the object was registered with.</param>
    /// <returns>
    /// The requested service instance.
    /// </returns>
    public object GetInstance(Type serviceType, string key)
    {
        return _unityContainer.Resolve(serviceType, key);
    }

    /// <summary>
    /// Get an instance of the given <paramref name="serviceType" />.
    /// </summary>
    /// <param name="serviceType">Type of object requested.</param>
    /// <returns>
    /// The requested service instance.
    /// </returns>
    public object GetInstance(Type serviceType)
    {
        return _unityContainer.Resolve(serviceType);
    }

    /// <summary>
    /// Gets the service object of the specified type.
    /// </summary>
    /// <param name="serviceType">An object that specifies the type of service object to get.</param>
    /// <returns>
    /// A service object of type <paramref name="serviceType" />.-or- null if there is no service object of type <paramref name="serviceType" />.
    /// </returns>
    public object GetService(Type serviceType)
    {
        return _unityContainer.Resolve(serviceType);
    }
}
Nothing too tricky here.


Step 2)

I followed the ViewModelLocater.cs route, so create on of those, register your DI i.e "ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));" like below in the contructor with all your registrations.

public class ViewModelLocator
{
    static IEnumerable<ContainerRegistration> registrations;
    /// <summary>
    /// Initializes the <see cref="ViewModelLocator"/> class.
    /// </summary>
    static ViewModelLocator()
    {
        var container = new UnityContainer();
        ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));

        // You can do automatic loading of all your registrations, but can be expensive on the loading times,
        // but I chose the manual route, the reflection was perf hit for me

        container.RegisterType<INavigationService, NavigationService>();
        //container.RegisterType<IDataService, DataService>();
        //container.RegisterType<IAppStateService, AppStateService>();
        //container.RegisterType<ILoggerFacade, LoggerService>();
        //container.RegisterType<ISystemDbService, SystemDbService>();

        container.RegisterType<HomeViewModel>();
        container.RegisterType<LoginViewModel>();


        registrations = container.Registrations;  // is for later use to query meta information
      }
/// <summary>
    /// Gets the home view model.
    /// </summary>
    /// <value>
    /// The home view model.
    /// </value>
    [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This non-static member is needed for data binding purposes.")]
    public HomeViewModel Home
    {
        get
        {
            return ServiceLocator.Current.GetInstance<HomeViewModel>();
        }
    }

    /// <summary>
    /// Gets the login view model.
    /// </summary>
    /// <value>
    /// The login view model.
    /// </value>
    [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This non-static member is needed for data binding purposes.")]
    public LoginViewModel Login
    {
        get
        {
            return ServiceLocator.Current.GetInstance<LoginViewModel>();
        }
    }
}

Step 3) In your app.xaml, add your viewmodellocator, this will be for your views and blendability

<Application x:Class="Rms.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:vm="clr-namespace:XXX.ViewModel"
         d1p1:Ignorable="d" 
         StartupUri="View/MainWindowView.xaml">
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
            <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
    </ResourceDictionary>
</Application.Resources>
</Application>


Step 4) In your viewmodels

you can get an instance to your vm like so

var vm = ServiceLocator.Current.GetInstance<LoginViewModel>();


Note, this is just a basic example. The real fun is the extensible stuff like the interception of calls, events etc on your registered instance, for example I'm doing transparent logging on my viewmodels, with completely no logging code on the viewmodels.

Hope this helps you.

Regards
PrakashZa
Dec 5, 2013 at 10:21 AM
Thanks Praskashza, really it's working

Sent from my iPhone
Venkat

Dec 5, 2013 at 9:38 PM
I do almost the exact same thing, but I do not do not do my container registrations in the ViewModelLocator class.., but rather in my App constructor or another bootstrapper class.

For example, why would I have my registrations for ISomeService inside my ViewModelLocator class? ViewModelLocator should only ask the container to resolve the ViewModels_ that are used... not register the other services/classes....