Skip to content

SystemResources depends on DependencyService #24216

Open

Description

Description

When trying to run unit tests and using the MauiAppBuilder after resolving the Application and while creating the Window a NullReferenceException is encountered.

  Message: 
System.NullReferenceException : Object reference not set to an instance of an object.

  Stack Trace: 
<.ctor>b__7_0()
Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
Lazy`1.CreateValue()
Application.get_SystemResources()
ResourcesExtensions.GetMergedResources(IElementDefinition element)
Element.SetParent(Element value)
Element.OnChildAdded(Element child)
Element.AddLogicalChild(Element element)
Application.AddWindow(Window window)
IApplication.CreateWindow(IActivationState activationState)

This error is a result of the following lines of code:

#pragma warning disable CS0612 // Type or member is obsolete
_systemResources = new Lazy<IResourceDictionary>(() =>
{
var systemResources = DependencyService.Get<ISystemResourcesProvider>().GetSystemResources();
systemResources.ValuesChanged += OnParentResourcesChanged;
return systemResources;
});
#pragma warning restore CS0612 // Type or member is obsolete

This isn't initially obvious as to either the problem or the solution as it requires implementing "Obsolete" code in order for a very basic scenario to work in a Unit Test.

Steps to Reproduce

var mauiBuilder = MauiApp.CreateBuilder()
    .UseMauiApp<Application>();
mauiBuilder.Services.AddTransient<IWindowCreator, WindowCreator()
    .AddTransient<TestPage>();

var mauiApp = mauiBuilder.Build();
var app = mauiApp.Services.GetRequiredService<IApplication>();
var activationState = new ActivationState(new MauiContext(mauiApp.Services));
var window = app.CreateWindow(activationState);

private record WindowCreator(TestPage Page) : IWindowCreator
{
    public Window CreateWindow(Application app, IActivationState? activationState) => new Window(Page);
}

public class TestPage : ContentPage
{
    public TestPage()
    {
        Title = nameof(TestPage);
        Content = new Label { Text = "Unit Tests" };
    }
}

Link to public reproduction project repository

No response

Version with bug

8.0.80 SR8

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

Other (Tizen, Linux, etc. not supported by Microsoft directly)

Affected platform versions

No response

Did you find any workaround?

While this technically does work... it shouldn't be something that is needed just to run Unit Tests.

#pragma warning disable CS0612 // Type or member is obsolete
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Internals;
using Prism.Ioc.Tests.Mocks;

[assembly: Dependency(typeof(MockResourcesProvider))]
[assembly: Dependency(typeof(MockFontNamedSizeService))]

namespace Prism.Ioc.Tests.Mocks;

[Obsolete]
internal class MockResourcesProvider : ISystemResourcesProvider
{
    public IResourceDictionary GetSystemResources()
    {
        var dictionary = new ResourceDictionary();
        Style style;
        style = new Style(typeof(Label));
        dictionary[Device.Styles.BodyStyleKey] = style;

        style = new Style(typeof(Label));
        style.Setters.Add(Label.FontSizeProperty, 50);
        dictionary[Device.Styles.TitleStyleKey] = style;

        style = new Style(typeof(Label));
        style.Setters.Add(Label.FontSizeProperty, 40);
        dictionary[Device.Styles.SubtitleStyleKey] = style;

        style = new Style(typeof(Label));
        style.Setters.Add(Label.FontSizeProperty, 30);
        dictionary[Device.Styles.CaptionStyleKey] = style;

        style = new Style(typeof(Label));
        style.Setters.Add(Label.FontSizeProperty, 20);
        dictionary[Device.Styles.ListItemTextStyleKey] = style;

        style = new Style(typeof(Label));
        style.Setters.Add(Label.FontSizeProperty, 10);
        dictionary[Device.Styles.ListItemDetailTextStyleKey] = style;

        return dictionary;
    }
}

[Obsolete]
public class MockFontNamedSizeService : IFontNamedSizeService
{
    public double GetNamedSize(NamedSize size, Type targetElement, bool useOldSizes)
    {
        return size switch
        {
            NamedSize.Default => 12,// new MockFontManager().DefaultFontSize,
            NamedSize.Micro => (double)4,
            NamedSize.Small => (double)8,
            NamedSize.Medium => (double)12,
            NamedSize.Large => (double)16,
            _ => throw new ArgumentOutOfRangeException(nameof(size)),
        };
    }
}
#pragma warning restore CS0612 // Type or member is obsolete

Relevant log output

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    area-testingUnit tests, device testsmigration-compatibilityXamarin.Forms to .NET MAUI Migration, Upgrade Assistant, Try-Convertt/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions