InspiredBy
InspiredBy

Reputation: 4320

Constructor Injection in Xamarin Froms .xaml.cs file classes

I want to take advantage of dependency injection in my Xamarin project but can't get constructor injection to work in C# classes behind XAML views. Is there any way to do it ? I've seen guides how to setup dependency injections in View Models, to later use them as repositories but that doesn't work for me.

So far I tried Ninject and Unity.

Code: This is the service I want to use inside of my PCL project:

public class MyService : IMyService
{
    public void Add(string myNote)
    {
        //Add Note logic
    }
}

Interface:

 public interface IMyService
 {
    void Add(string myNote);
 }

Unity setup in App.Xaml:

public App ()
{
   InitializeComponent();

    var unityContainer = new UnityContainer();
     unityContainer.RegisterType<IMyService, MyService>();

    var unityServiceLocator = new UnityServiceLocator(unityContainer);              
    ServiceLocator.SetLocatorProvider(() => unityServiceLocator);

    MainPage = new MainMasterMenu(); //<-- feel that I'm missing something here as I shouldn't be creating class instances with DI, right ?
}

Usage that I'd like to see. This is .CS file behind a XAML starting page:

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainMasterMenu : MasterDetailPage
{
    private IMyService _myService;
    public MainMasterMenu(IMyService myService)
    {
      _myService = myService
    }

   private void SomeFormControlClickEvent(object sender, ItemChangedEventArgs e)
    {
     _myService.Add("hi");
    }
}

Upvotes: 1

Views: 731

Answers (1)

Paul Kertscher
Paul Kertscher

Reputation: 9742

For that simple example creating the MainMasterMenu directly would be no issue, but you would have to pass the reference to your service

MainPage = new MainMasterMenu(unityContainer.Resolve<IMyService>());

But this would mean that you'll have to change that line every time the constructor of MainMasterMenu changes. You could circumvent this by registering the MainMasterMenu, too.

unityContainer.RegisterType<MainMasterMenu>();
...
MainPage = unityContainer.Resolve<MainMasterPage>();

Anyway, anytime you want to navigate to another page, which needs any dependency registered with unity, you'll have to make sure to resolve its dependencies properly, which requires (at least indirect) access to the unity container. You could pass a wrapper that encapsules the access to unity

interface IPageResolver
{
    T ResolvePage<T>()
        where T : Page;
}

and then implement that resolver with unity

public class UnityPageResolver
{
    private IUnityContainer unityContainer;

    public UnityPageResolver(IUnityContainer unityContainer)
    {
        this.unityContainer = unityContainer;
    }

    public T ResolvePage<T>()
        where T : Page // do we need this restriction here?
    {
        return unityContainer.Resolve<T>();
    }
}

This gets registered with unity

unityContainer.RegisterInstance<IUnityContainer>(this);
unityContainer.RegisterType<IPageResolver, UnityPageResolver>();

But you should have a look at the Prism library (see here) that solves many of the issues (e.g. it provides an INavigationService that lets you navigate to other pages without caring about the dependencies and it provides facilities to resolve viewmodels automatically, including dependencies).

Upvotes: 2

Related Questions