maui: Can't use App.xaml resources after Dependency Injection .NET MAUI
Description
I have an application that uses mvvm pattern with Community Toolkit features. I am trying to use Dependency Injection to use an ApiService with its interface in my viewmodels, but after following the steps described here, I can’t access to App.xaml Resources (specifically colors), the intellisense works when I am writing code in XAML, but it doesn´t work after running. It is important to notice that I was using colors from resources correctly before trying to use Dependency Injection and changing the ViewModel - View linking method to the one described here, but I was unable to use ApiService. Here is my code.
Steps to Reproduce
App.xaml.cs (Login page is my first page):
public App(LoginPage page)
{
InitializeComponent();
MainPage = page;
}
LoginPage.xaml.cs
public partial class LoginPage : ContentPage
{
public LoginPage(LoginPageViewModel loginPageViewModel)
{
BindingContext = loginPageViewModel;
InitializeComponent();
}
}
LoginPageViewModel
public LoginPageViewModel(IApiService apiService)
{
_apiService = apiService;
Opacity = 1f;
IsEnabled = true;
IsRunning = false;
}
LoginPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NewScholarApp.Views.LoginPage"
Title="LoginPage"
xmlns:vm="clr-namespace:NewScholarApp.ViewModels"
x:DataType="vm:LoginPageViewModel"
BackgroundColor="{StaticResource GreenSchool}">
RegisterViews and ViewModels
public static MauiAppBuilder ConfigureViews(this MauiAppBuilder mauiAppBuilder)
{
mauiAppBuilder.Services.AddTransient<LoginPage>();
return mauiAppBuilder;
}
public static class RegisterViewModels
{
public static MauiAppBuilder ConfigureViewModels(this MauiAppBuilder mauiAppBuilder)
{
mauiAppBuilder.Services.AddSingleton<LoginPageViewModel>();
return mauiAppBuilder;
}
}
Link to public reproduction project repository
https://github.com/luis95gr/SampleProject
Version with bug
7.0 (current)
Last version that worked well
Unknown/Other
Affected platforms
Android, I was not able test on other platforms
Affected platform versions
Android 12.1
Did you find any workaround?
Don’t use any StaticResource
Relevant log output
Microsoft.Maui.Controls.Xaml.XamlParseException: 'Position 8:14. StaticResource not found for key GreenSchool'
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 8
- Comments: 31 (7 by maintainers)
I encountered this same issue when upgrading to MAUI and switching to the new DI style. This is a bug.
The options for workarounds I found are the following:
What is happening is that if you use DI to register ContentPage elements and maybe other UI elements, you will not be able to access static resources declared in your App.xaml file. I’m sure the technical reasons involve the app lifecycle assumptions deep within MAUI. A UI element resolved via constructor injection is initialized very early in the app or page lifecycle, and it can only access those resources later on. I believe I saw other bugs also logged about this, and maybe it will get fixed.
But essentially you can get around this by injecting IServiceProvider into the App class, and then using that to resolve the MainPage inside the App class, thereby forcing the resolution of the page element to occur later than the constructor injection would’ve resolved it. I demonstrated this approach in the comment above yours.
Another benefit of that approach is if you were injecting multiple modals/elements/popups into the same class, you would be able to ensure that those modals/elements/popups were only instantiated if needed, and only when they are needed. So you could paint the screen quicker, use less memory, etc.
I experienced the same behavior (“StaticResource not found for key ShellMenuItemTemplate”) when injecting a custom service into my MainPage (derived from Shell) as part of my pet project powered by DevExpress .NET MAUI project templates and controls. Your solution certainly worked (
MainPage = serviceProvider.GetRequiredService<MainPage>();) and it is fine with me - thank you for documenting it here for others.Please just consider the following documentation enhancements for the .NET MAUI developer community (not urgent):
MainPage = mainPage;) - probably your tech writers can add a note there. CC @EilonThank you, Everyone.
I hit this issue also. I fixed in this way:
Hi @luis95gr I have a workaround for it, you can create service helper class with this code
and you can use it where ever it is required(lazy loading)
Do not register pages in builder.Services, just register services and Viewmodels
Just for reference, for styles and colors, which are defined in a
ResourceDictionary, I use{StaticResource ...}, for classes and static resources from code behind, I’m using{x:Static ...}. Haven’t had any problems yet, even withDI.The
MaterialFontIconsclass looks like that.https://github.com/AndreasReitberger/SharedMauiXamlStyles/blob/main/src/SharedMauiXamlStylesLibrary/Models/FontIcons/MaterialIcons.cs
Maybe this helps!
So we should use service locator antipattern because MAUI team cannot implement DI properly? No thanks
I could not get the workaround to work for me. Therefore, I have no choice but to remove all static resources in order to continue my migration efforts. Might be another issue in my migrated codebase that is causing this in combination but as for almost every error I encounter, the error messages and callstacks are completely useless.
This migration from a medium sized enterprise Xamarin.Forms app was the worst experience I’ve ever had as a developer and that truly means a lot. I used to be a Xamarin and especially Xamarin.Forms advocate. I have been in from the very beginning and gladly paying $999 yearly for Xamarin.iOS.
These days I’m downright frustrated with how tedious the porting and how immature the runtime is. It’s a disastrous state and the way I see it, nothing has really changed with .NET 8.
I think using a fully MVVM and DI-centric approach in the MAUI project templates would ensure people don’t run into this.
IServiceProvider would be used by default to grab the initial MainPage and the ViewModel for the AppShell.
We cannot inject the “MainPage” into App ctor if MainPage uses static resources from App.xaml (or further references xamls, e.g. Styles.xaml or Colors.xaml). The instance of MainPage is created by DI just before it is injected into App.xaml.cs’ constructor. We need to run App.xaml.cs’ InitializeComponent() method before we can run MainPage’s InitializeComponent() method.
I would expect .NET MAUI to provide better support for Page/ViewModel handling with DI.
This StackOverflow question is another example of the problem.
As I explain in first comment there, the problem is a direct consequence of how DI works.
The assumption in XamarinForms/Maui has always been that App gets initialized, BEFORE any page is constructed. Using DI to inject a page into App constructor breaks that design assumption.
@symbiogenesis
Thanks a lot for pointing the root cause out. I did not know that actually the instance is lazily created when you call
GetService()/GetRequiredService(), I assumed it is already created anyways and is just picked from the pile of the services living in theIServiceProvider. Injecting IServiceProvider really helped solving the issue. My use case was:Injecting
AppShellintoAppbecauseAppShellneeds injectedAppShellViewModelto be able to control menu items visibility via bound properties.Trying to achieve:
With this code, resolving the resource failed with the error shown below:
With this code, resolving the resource works fine:
Modified
https://github.com/symbiogenesis/ServiceProviderInjection
If you use DI for everything, you can just use constructor injection to resolve IServiceProvider from anywhere in your application, and you shouldn’t need a static IServiceProvider exposed.
Except maybe for integration testing purposes, and then you can use an ifdef for that case.
We’ve moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
Try to change the order like this: