user2915962
user2915962

Reputation: 2711

MVVM Dependency Injection cross-platform

Earlier I wrote this question regarding a way to have my MainViewModel implement an interface depending of what project is running in my Universal App.

Choose interface implementation in universal app depending on platform

I have after this implemented MVVM light which lets me register my VM´s like this:

SimpleIoc.Default.Register<MainViewModel>();

It also offers the oppurtunity to register a VM with an interface in the CTOR like this:

SimpleIoc.Default.Register<IDataService, DataService>();

The setup is as follows:

I have 2 projects, Windows and WindowsPhone. In my VM, which resides in a PCL I have my MainViewModel that takes an ICameraCaller in the ctor. The projects both have a class that implements ICameraCaller and depending on which project i run I would like to pass the "right" implementation of the CameraCaller to the CTOR.

So I´ve been told that DI is the way to go. This is a fairly new topic for me and I´ve been stuck for a long time.

To me it sounds like it should be a pretty comman task? To pass an implementation of an interface to a VM depending on which projects is running.

Here is the ctor for the MainViewModel:

private ICameraCaller _cameraCaller;

public MainViewModel(ICameraCaller cameraCaller)
{
    _cameraCaller = cameraCaller;
}

I do not understand how Its possible for me to register an Interface to my viewmodel in the ViewModelLocator considering that I do not know what implementation that will be used. Even if I did know. I cannot access the Implementations in the other projects. I must be going about this problem all wrong.

Upvotes: 1

Views: 236

Answers (1)

fex
fex

Reputation: 3558

First of all: use portable version of mvvm light MVVMLight.portable. I also suggest you to use some more advanced IoC container - personally I prefer Ninject (which also have portable version called Ninject.Portable) - it suits all my needs. Get it from Nuget Package Manager.

Second - create class called ViewModelLocator (different for Windows Phone and Windows Modern) it will act as your so-called ApplicationCore (place where dependency injection container sits).

Example:

public class ViewModelLocator
{
    private readonly IKernel _dependenciesContainer;

    public ViewModelLocator() 
    {
         _dependenciesContainer = new StandardKernel(new ApplicationKernelModule());
    }

    public MainViewModel Main { get { return _dependenciesContainer.Get<MainViewModel>() } }
}

public class ApplicationKernelModule : NinjectModule
{ 
    public override void Load()
    {
        Bind<ICameraCaller>().To<DesktopCameraCaller>();  
    }
} 

public class DesktopCameraCaller : ICameraCaller
{
     // here lies Windows Modern implementation of camera caller
}

In App.xaml add your ViewModelLocator as StaticResource :

<Application
x:Class="YourApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YouNamespace"
xmlns:viewModel="using:ViewModelLocatorNamespace">

<Application.Resources>
    <ResourceDictionary>

        <viewModel:ViewModelLocator x:Key="ViewModelLocator"/>
    </ResourceDictionary>
</Application.Resources>

And in your MainView add binding to proper ViewModel:

DataContext={Binding Source={StaticResource ViewModelLocator}, Path=Main}

Repeat exactly same steps for your Windows Phone project but in ApplicationModule make bind to your Windows Phone implementation, example: Bind<ICameraController>().To<WindowsPhoneCameraController>().

What does happen there? You use Inversion of Control library called Ninject to "bind" interfaces into concrete classes. That means - when you call IKernel.Get<Type>() and while creating object Ninject see anywhere in constructor ICameraControl it will create there new ModernCameraControl object. You created ViewModelLocator to make simple place to store your "Dependency Injection Resolver" (called IKernel) - so you don't have to use ugly service locator pattern and static reference to Kernel everywhere. Of course I described it in simplified version - with Ninject you can do other awesome things like scoping (for instance you can register interface to class which would be singleton - same object instation everywhere where binded), you can add conditional logic while class being injected (check Bind<T>().To<T>().WhenInjectedTo/WhenInjectedExactlyTo, Bind<T>().ToMethod(..) etc.).

Upvotes: 4

Related Questions