My Goal is to NOT create dependencies between ViewModels.
I have .NET 7 MAUI project.
The situation is simple.
I have the MainPage
and I linked ViewModel MainPageViewModel
to it using DI
services.AddSingleton<MainPageViewModel>();
services.AddSingleton<MainPage>(provider =>
{
return new MainPage()
{
BindingContext = provider.GetRequiredService<MainPageViewModel>(),
};
});
It is working fine, the ViewModel is created and it is assigned to as BindingContext
of the MainPage
, Great.
Now, When I put another Child View inside the MainPage
like this
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyNamespace"
x:Class="MyNamespace.MainPage">
<MyNamespace:ChildView />
</ContentPage>
and I register it with its ViewModel like this
services.AddSingleton<ChildViewModel>();
services.AddSingleton<ChildView>(provider =>
{
return new ChildView()
{
BindingContext = provider.GetRequiredService<ChildViewModel>(),
};
});
The MainPage
create the ChildView
directly (by calling its parameterless constructor) without consulting the DI Container
This is causing the lack of Binding for the ViewModel.
ChildViewModel
inside MainPageViewModel
and pass it.MainPage
is like this<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Trex.Mobile.App.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyNamespace"
Shell.FlyoutBehavior="Disabled">
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />
</Shell>
and by using this way, the DI container is used.
Can I use something like that for my ChildView
or should I change something fundamentally
I don't think it's possible to create the ChildView
using the DI container, because Shell doesn't instantiate it.
Since the MainPage
instance is created by Shell, you can use constructor injection:
public partial class MainPage : ContentPage
{
public MainPage(MainPageViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}
Then, as I have described in this answer, you can create a ServiceHelper class like so:
public static class ServiceHelper
{
public static IServiceProvider Services { get; private set; }
public static void Initialize(IServiceProvider serviceProvider) =>
Services = serviceProvider;
public static T GetService<T>() => Services.GetService<T>();
}
The ChildView
is instantiated by the MainPage
and cannot use Shell's constructor injection, but you can use the ServiceHelper
from above to resolve the ChildViewModel
:
public partial class ChildView : ContentView
{
public ChildView()
{
InitializeComponent();
BindingContext = ServiceHelper.GetService<ChildViewModel>();
}
}
Finally, register all dependencies and setup the ServiceHelper
:
builder.Services.AddSingleton<MainPage>();
builder.Services.AddSingleton<MainPageViewModel>();
builder.Services.AddSingleton<ChildView>();
builder.Services.AddSingleton<ChildViewModel>();
var app = builder.Build();
//we must initialize our service helper before using it
ServiceHelper.Initialize(app.Services);
return app;