Sam
Sam

Reputation: 30324

Injecting services into view models in .NET MAUI app

I'm trying to understand how to implement dependecy injection in a .NET MAUI app.

I have a service class -- and its interface -- that handle my REST calls that looks like this:

public class MyRestApiService : IMyRestApiService
{
   public async Task<string> Get()
   {
      // Do someting
   }
}

I then place this in my DI container in MauiProgram.cs:

builder.Service.AddTransient<IMyRestApiService, MyRestApiService>();

I also have a view model that I will use for my MainPage.xaml. The question is, if I do a constructor injection of my service, the XAML doesn't seem to like it.

The MainPageViewModel looks like this:

public class MainPageViewModel : BaseViewModel
{
   IMyRestApiService _apiService;
   public MainPageViewModel(IMyRestApiService apiService)
   {
      _apiService = apiService;
   }
}

When I tried to define MainPageViewModel as the view model for MainPage.xaml as below, I get an error:

<ContentPage.BindingContext>
   <vm:MainPageViewModel />
</ContentPage.BindingContext>

The error reads:

Type MainPageViewModel is not usable as an object element because it is not public or does not define a public parameterless constructor or a type converter.

How do I inject my services into my view models?

Upvotes: 21

Views: 18065

Answers (2)

Gerald Versluis
Gerald Versluis

Reputation: 34083

You will want to resolve everything basically from the first page for everything to fall in place and for dependency injection to work.

Have a look at this example: https://github.com/jfversluis/MauiDependencyInjectionSample

You will want to register your services, view models and views. In your case, in your MauiProgram.cs add:

// Change scopes as needed, this seems to make sense
builder.Service.AddTransient<MainPage>();
builder.Service.AddTransient<MainPageViewModel>();
builder.Service.AddSingleton<IMyRestApiService, MyRestApiService>();

Then in your App.xaml.cs also start injecting your (main) page:

public partial class App : Application
{
    public App(MainPage page)
    {
        InitializeComponent();

        MainPage = page;
    }
}

Now in your MainPage.xaml.cs add a constructor like this:

public LoginPage(MainPageViewModel viewModel)
{
    InitializeComponent();

    BindingContext = viewModel;
}

From there everything should follow suit and be connected. What you are trying to do with

<ContentPage.BindingContext>
   <vm:MainPageViewModel />
</ContentPage.BindingContext>

Is basically setting the BindingContext through the property. You can, but then you'll have to specify the parameters yourself and resolve them from the dependency injection container somehow which typically you don't want to do.

Upvotes: 22

LoRdPMN
LoRdPMN

Reputation: 532

To inject your view model into your view you actually need to do it in its constructor, in code behind, like this:

public partial class LoginPage : ContentPage {
    public LoginPage(ILoginViewModel loginViewModel) {
        BindingContext = loginViewModel;
        InitializeComponent();
    }
}

Also you have to register views that use dependency injection:

builder.Service.AddTransient<LoginPage>();

Afaik you can't instantiate a view model with DI in XAML like you are doing

Upvotes: 3

Related Questions