0

I'm trying to pass a service to a component constructor in MAUI:

public partial class MyComponent: ContentView
{
    public MyComponent(MyService service)
    {
        InitializeComponent();

        data = service.getData();
    }
}

But it throws error and asks to add a default constructor:

public partial class MyComponent: ContentView
{

    public MyComponent() { }

    public MyComponent(MyService service)
    {
        InitializeComponent();

        data = service.getData();

    }
}

I added singletons to pass the service to the component:

builder.Services.AddSingleton<MyComponent>();
builder.Services.AddSingleton<MyService>();

I also tried this method:

builder.Services.AddSingleton(sp => new MyComponent(sp.GetService<MyService>()));

None of them worked. Only default constructor called

I use the component in a page like this:

<ContentPage ...
             xmlns:components="clr-namespace:MyApp.Components">
        <components:MyComponent/>
</ContentPage>

How do I pass a service to a component?

Ken White
  • 123,280
  • 14
  • 225
  • 444
1baz
  • 148
  • 1
  • 9

2 Answers2

1

You could not directly use dependency injection in a custom control.

See this issue on Github: Dependency Injection for Custom Controls. It's still enhancement under consideration and you could follow this issue.

There should be some workarounds. Just as tripjump comment in above issue, you could attach a bindable property and inject the viewModel from MainPage through this property. I just made a small demo for you.

For MyComponent control:

public partial class MyComponent : ContentView
{

    public static readonly BindableProperty ServiceProperty = BindableProperty.Create(nameof(Service),typeof(MyService), typeof(MyComponent),propertyChanged: OnServiceChanged);

    static void OnServiceChanged(BindableObject bindable, object oldValue, object newValue)
    {
    // Property changed implementation goes here
        MyService a = newValue as MyService;
    }

    public MyService Service
    {
        get => (MyService)GetValue(MyComponent.ServiceProperty);
        set => SetValue(MyComponent.ServiceProperty, value);
    }

    public MyComponent()
    {
        InitializeComponent();
    }
}

For MainPage which consumes MyComponent:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         ...
         x:Name="this">

<components:MyComponent BindingContext="{x:Reference this}" MyService="{Binding BindingContext.service}"/>

For MainPageViewModel which is BindingContext of MainPage:

public class MainPageViewModel
{
    public MyService service { get; set; }

    public MainPageViewModel(MyService myService)
    {
        service = myService;
    }
}

And also add service in MauiProgram:

    builder.Services.AddSingleton<MainPage>();
    builder.Services.AddSingleton<MainPageViewModel>();
    builder.Services.AddSingleton<MyService>();

Hope it works for you.

Liqun Shen-MSFT
  • 3,490
  • 2
  • 3
  • 11
1

You can also use dependency injection like in there.

Create a ServiceProvider class :

public static class ServiceProvider
{
    public static TService GetService<TService>()
        => Current.GetService<TService>();

    public static IServiceProvider Current
        =>
#if WINDOWS10_0_17763_0_OR_GREATER
            MauiWinUIApplication.Current.Services;
#elif ANDROID
            MauiApplication.Current.Services;
#elif IOS || MACCATALYST
            MauiUIApplicationDelegate.Current.Services;
#else
            null;
#endif
}

Then you can simply use it in any component constructor :

_Contexte = ServiceHelper.GetService<Contexte>();

As for why you can't pass parameter to XAML components it's because they are converted to C# code and then automatically process. If the constructor has argument the mechanism handling the conversion wont know which instance to pass to the constructor (unless microsoft decide to handle DI, which may happend at some point).

Poulpynator
  • 716
  • 5
  • 13