maui
maui copied to clipboard
Dependency Injection for Custom Controls
Description
I was playing with the Dependency Injection in Preview 12 and noticed that if I have a custom control that I want to inject a view model into, it's not currently possible. When I place the control inside another ContentPage the xaml warns that the control doesn't have a default constructor. This may just be a limitation and there are definitely workarounds but I figured I would put it on the radar because it'd be a nice thing to button up on what is a great feature.
Public API Changes
Not sure there are public API changes, I'm not totally familiar with how the xaml deals with constructors, but if it could determine if a constructor has services that have been registered with the service collection and use that constructor without having to interact with the code behind, that'd be pretty nice.
Intended Use-Case
Custom Controls that dependency inject view models which themselves dependency inject services.
I have used dependency injection in .net MAUI to inject a state management object in a custom control. For a viewmodel it should look like this: CustomControl.xaml file: <ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui" ... x:Name="this">
CustomControl.xaml.cs file: public static readonly BindableProperty ViewModelProperty = BindableProperty.Create(nameof(ViewModel), typeof(IViewModel), typeof(CustomControl));
public IViewModel ViewModel
{
get => (ViewModel)GetValue(CustomControl.ViewModelProperty);
set => SetValue(CustomControl.ViewModelProperty, value);
}
MainPage.xaml : <ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui" ... x:Name="this"> ..... <local:CustomControl Grid.Row="0" BindingContext="{x:Reference this}" ViewModel="{Binding BindingContext}"> </local:CustomControl>
Injection: MainPage.xaml.cs: public MainPage(IViewModel viewModel) { InitializeComponent(); BindingContext = viewModel; } MauiProgram.cs: builder.Services.AddSingleton<IViewModel, ViewModel>(); builder.Services.AddSingleton<MainPage>();
Just pointing out that this particular example is just passing the MainPage ViewModel along to the custom control. But I could see where the MainPage ViewModel injected the Control ViewModel (which is independent from the MainPage ViewModel) as a property, then the Control would bind to that ViewModel.
All of this is to say that I know there are workarounds and this is definitely icing on the cake and not the batter, but it would be pretty awesome if the dependency service and the xaml could sync up and recognize that a custom control is looking for a dependency to be injected and if the dependency service has something valid registered go ahead and use that constructor directly in the xaml without all the rigmarole of setting up a new dependency property. Still love this feature regardless.
100% support for this. Without this, it makes it hard work to handle complex control trees
Why should that not be supported? It is a must-feature.
Absolutely needed! Please make this a priority.
A simple way to work around this is by assigning a BindingContext to the bound model in the parent contentpage like this BindingContext="{Binding}". Say you have a ContentPage with a CollectionView inside and the DataTemplate of the CollectionView is your custom control. You can assign the context to your custom control like this:
<CollectionView Grid.Column="1" ItemsSource="{Binding RightSideDayCards}">
<CollectionView.ItemTemplate>
<DataTemplate>
<controls:CalendarDayCard BindingContext="{Binding}"></controls:CalendarDayCard>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Then simply be sure to not override that context anywhere in the xaml of your custom control.
I was bothered by this problem for a long time, only to find out that is was because MAUI did not yet support DI for custom controls. I think this feature is absolutely needed as well!
Whle the original question is about BindingContext, that is not the only need for DI.
Consider a custom control that relies on some service, MyService.
The containing page has no reason to know about this.
Consider:
public partial class MyView : ContentView
{
public MyView(MyService myService)
{
InitializeComponent();
...
}
}
If attempt to use in XAML:
... xmlns:local="clr-namespace:MyApp" ...
`<local:MyView />
Get Error XLS0507 Type 'MyView' is not usable as an object element because it is not public or does not define a public parameterless constructor or a type converter.
And of course if you add a parameterless constructor, then GetService will use that one instead of the desired one, ruining the use of GetService elsewhere.
NOTE: the C# GetService version works:
public class MyPage : ContentPage
{
public MyPage()
{
Content = App.Services.GetService<MyView>(); // App.Services property defined during App construction.
}
}
Ideally, XAML would recognize that MyView is injectable, and perform GetService.
OR at least some way to represent GetService call in XAML.
NOTE: A work-around is to give up on DI injection of parameter, and call GetService inside the constructor:
public MyView()
{
InitializeComponent();
MyService myService = App.Services.GetService<MyService>(); // App.Services property defined during App construction.
...
}
@Okanalagas1 I haven't follow MAUI for a while, so I dont know if MAUI support DI for custom controls now. In my project, I use this method:
public ItemBarcodeControl()
{
InitializeComponent();
IsVisibleButtonGrid = "true";
IsVisibleDetailGrid = "false";
var factory= ServiceProvider.GetService<IHttpClientFactory>();
_client = factory?.CreateClient(AppConstants.ApiName);
}
And then you should register service in the MauiProgram.cs as follows:
builder.Services.AddSingleton((IServiceProvider) =>
{
var factory = new IHttpClientFactory ();
// Do something to init factory.
return factory;
});
Hope it can be helpful to you.
@FcAYH It looks like this ticket is still open without resolution, thanks for the workaround.
I get the error for my ContentView control, and I'm also unable to do DI with it, if I have the following code, I get this error from the page: (everything is DI'ed)
using MauiKanApp.ViewModel.Login;
namespace MauiKanApp.Controls;
public partial class LoginControl : ContentView { public LoginControl(LoginControlViewModel viewModel) { InitializeComponent();
//https://github.com/dotnet/maui/issues/4538
this.BindingContext = viewModel;
}
}
within the page:
..... <Shell.FlyoutFooter> <StackLayout Padding="20"> <controls:LoginControl IsVisible="True" /> <Button BackgroundColor="Red"
..... etc
ERROR: Severity Code Description Project File Line Suppression State Error XLS0507 Type 'LoginControl' is not usable as an object element because it is not public or does not define a public parameterless constructor or a type converter............... etc
I get the error for my ContentView control, and I'm also unable to do DI with it, if I have the following code, I get this error from the page: (everything is DI'ed)
using MauiKanApp.ViewModel.Login;
namespace MauiKanApp.Controls;
public partial class LoginControl : ContentView { public LoginControl(LoginControlViewModel viewModel) { InitializeComponent();
//https://github.com/dotnet/maui/issues/4538 this.BindingContext = viewModel; }}
within the page:
..... <Shell.FlyoutFooter> <controls:LoginControl IsVisible="True" /> <Button BackgroundColor="Red"
..... etc
ERROR: Severity Code Description Project File Line Suppression State Error XLS0507 Type 'LoginControl' is not usable as an object element because it is not public or does not define a public parameterless constructor or a type converter............... etc
Yes, MAUI is still not support DI for custom controls.
Yes, MAUI is still not support DI for custom controls.
I have learned something new today.
Oh? Is it, what's interesting? Can DI related issues be solved?
I have learned something new today. @plppp2001
Oh? Is it, what's interesting? Can DI related issues be solved?
I have learned something new today. @plppp2001
I didn't know that with Maui controls, we can't DI them. 😶🌫️
okok
from F_CIL : May you have a nice day.
From: Paul Astro @.> Sent: Thursday, January 25, 2024 1:01:56 AM To: dotnet/maui @.> Cc: F_CIL @.>; Mention @.> Subject: Re: [dotnet/maui] Dependency Injection for Custom Controls (Issue #4538)
Oh? Is it, what's interesting? Can DI related issues be solved?
I have learned something new today. @plppp2001https://github.com/plppp2001
I didn't know that with Maui controls, we can't DI them. 😶🌫️
— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/maui/issues/4538#issuecomment-1908554400, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AMYIJKHD7UXFSMDE76YN2KLYQE5AJAVCNFSM5NXCZ7NKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOJQHA2TKNBUGAYA. You are receiving this because you were mentioned.Message ID: @.***>