Skip to content

Commit ec09b17

Browse files
authored
Merge pull request #3239 from chowarth/feature/containerprovider
Add ContainerProvider for resolving dependencies in XAML
2 parents 66b1ab3 + a43fcc7 commit ec09b17

File tree

8 files changed

+229
-0
lines changed

8 files changed

+229
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#nullable enable
2+
namespace Prism.IoC;
3+
4+
/// <summary>
5+
/// Provides Types and Services registered with the Container
6+
/// </summary>
7+
/// <typeparam name="T">The type to Resolve</typeparam>
8+
/// <example>
9+
/// We can use this to build better types such as ValueConverters with full dependency injection
10+
/// <code>
11+
/// public class MyValueConverter : IValueConverter
12+
/// {
13+
/// private readonly ILogger _logger { get; }
14+
///
15+
/// public MyValueConverter(ILogger logger)
16+
/// {
17+
/// _logger = logger;
18+
/// }
19+
///
20+
/// public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
21+
/// {
22+
/// _logger.Log($"Converting {value.GetType().Name} to {targetType.Name}", Category.Debug, Priority.None);
23+
/// // do stuff
24+
/// }
25+
///
26+
/// public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
27+
/// {
28+
/// _logger.Log($"Converting back from {value.GetType().Name} to {targetType.Name}", Category.Debug, Priority.None);
29+
/// return null;
30+
/// }
31+
/// }
32+
/// </code>
33+
/// We can then simply use our ValueConverter or other class directly in XAML
34+
/// <![CDATA[
35+
/// <ContentPage xmlns:prism="clr-namespace:Prism.Ioc;assembly=Prism.Maui">
36+
/// <ContentPage.Resources>
37+
/// <ResourceDictionary>
38+
/// <prism:ContainerProvider x:TypeArguments="MyValueConverter" x:Key="myValueConverter" />
39+
/// <ResourceDictionary>
40+
/// <ContentPage.Resources>
41+
/// <Label Text="{Binding SomeProp, Converter={StaticResource myValueConverter}}" />
42+
/// </ContentPage>
43+
/// ]]>
44+
/// </example>
45+
public class ContainerProvider<T>
46+
{
47+
/// <summary>
48+
/// The Name used to register the type with the Container
49+
/// </summary>
50+
public string? Name { get; set; }
51+
52+
/// <summary>
53+
/// Resolves the specified type from the Application's Container
54+
/// </summary>
55+
/// <param name="containerProvider"></param>
56+
public static implicit operator T(ContainerProvider<T> containerProvider)
57+
{
58+
var container = ContainerLocator.Container;
59+
if (container == null) return default(T);
60+
if (string.IsNullOrWhiteSpace(containerProvider.Name))
61+
{
62+
return container.Resolve<T>();
63+
}
64+
65+
return container.Resolve<T>(containerProvider.Name);
66+
}
67+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Prism.DryIoc.Maui.Tests.Mocks;
2+
using Prism.DryIoc.Maui.Tests.Mocks.Events;
3+
using Prism.DryIoc.Maui.Tests.Mocks.ViewModels;
4+
using Prism.DryIoc.Maui.Tests.Mocks.Views;
5+
using Prism.Events;
6+
using Prism.IoC;
7+
8+
namespace Prism.DryIoc.Maui.Tests.Fixtures.IoC;
9+
10+
public class ContainerProviderTests(ITestOutputHelper testOutputHelper) : TestBase(testOutputHelper)
11+
{
12+
[Fact]
13+
public void CanResolveUnamedType()
14+
{
15+
var builder = CreateBuilder(prism => { });
16+
var app = builder.Build();
17+
var containerProvider = new ContainerProvider<ConcreteTypeMock>();
18+
19+
ConcreteTypeMock type = (ConcreteTypeMock)containerProvider;
20+
21+
Assert.NotNull(type);
22+
Assert.IsType<ConcreteTypeMock>(type);
23+
}
24+
25+
[Fact]
26+
public void CanResolvedNamedType()
27+
{
28+
var builder = CreateBuilder(prism => { });
29+
var app = builder.Build();
30+
var containerProvider = new ContainerProvider<ConcreteTypeMock>
31+
{
32+
Name = ConcreteTypeMock.Key
33+
};
34+
35+
ConcreteTypeMock vm = (ConcreteTypeMock)containerProvider;
36+
37+
Assert.NotNull(vm);
38+
Assert.IsType<ConcreteTypeMock>(vm);
39+
}
40+
41+
[Fact]
42+
public async Task ProvidesValueFromResourceDictionary()
43+
{
44+
var builder = CreateBuilder(prism =>
45+
{
46+
prism.RegisterTypes(containerRegistry =>
47+
{
48+
containerRegistry.RegisterForNavigation<MockXamlView, MockXamlViewViewModel>();
49+
})
50+
.CreateWindow(n =>
51+
n.CreateBuilder()
52+
.AddSegment<MockViewAViewModel>()
53+
.NavigateAsync());
54+
});
55+
var app = builder.Build();
56+
57+
var ea = app.Services.GetService<IEventAggregator>();
58+
var events = new List<string>();
59+
ea.GetEvent<TestActionEvent>().Subscribe((m) => events.Add(m));
60+
61+
var navigation = app.Services.GetService<INavigationService>();
62+
await navigation.CreateBuilder()
63+
.AddSegment<MockXamlViewViewModel>()
64+
.NavigateAsync();
65+
66+
Assert.Contains(events, e => e == "Convert");
67+
var window = GetWindow(app);
68+
Assert.NotNull(window.Page);
69+
70+
var xamlView = window.Page as MockXamlView;
71+
var viewModel = xamlView.BindingContext as MockXamlViewViewModel;
72+
73+
xamlView.TestEntry.Text = "Foo Bar";
74+
Assert.Contains(events, e => e == "ConvertBack");
75+
}
76+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Prism.DryIoc.Maui.Tests.Mocks;
2+
3+
internal class ConcreteTypeMock
4+
{
5+
public const string Key = "ConcreteTypeMock";
6+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Globalization;
2+
using Prism.DryIoc.Maui.Tests.Mocks.Events;
3+
using Prism.Events;
4+
5+
namespace Prism.DryIoc.Maui.Tests.Mocks.Converters;
6+
7+
internal class MockValueConverter : IValueConverter
8+
{
9+
private IEventAggregator _eventAggreator { get; }
10+
11+
public MockValueConverter(IEventAggregator eventAggreator)
12+
{
13+
_eventAggreator = eventAggreator;
14+
}
15+
16+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
17+
{
18+
_eventAggreator.GetEvent<TestActionEvent>().Publish("Convert");
19+
return value;
20+
}
21+
22+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
23+
{
24+
_eventAggreator.GetEvent<TestActionEvent>().Publish("ConvertBack");
25+
return value;
26+
}
27+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using Prism.Events;
2+
3+
namespace Prism.DryIoc.Maui.Tests.Mocks.Events;
4+
5+
internal class TestActionEvent : PubSubEvent<string>
6+
{
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Prism.DryIoc.Maui.Tests.Mocks.ViewModels;
2+
3+
internal class MockXamlViewViewModel : BindableBase
4+
{
5+
private string _test = "Initial Value";
6+
public string Test
7+
{
8+
get => _test;
9+
set => SetProperty(ref _test, value);
10+
}
11+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ContentPage
3+
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
5+
xmlns:prism="clr-namespace:Prism.IoC;assembly=Prism.Maui"
6+
xmlns:converters="clr-namespace:Prism.DryIoc.Maui.Tests.Mocks.Converters"
7+
x:Class="Prism.DryIoc.Maui.Tests.Mocks.Views.MockXamlView"
8+
Title="MockXamlView">
9+
10+
<ContentPage.Resources>
11+
<ResourceDictionary>
12+
<prism:ContainerProvider
13+
x:TypeArguments="converters:MockValueConverter"
14+
x:Key="mockValueConverter" />
15+
</ResourceDictionary>
16+
</ContentPage.Resources>
17+
18+
<StackLayout>
19+
<Entry x:Name="testEntry"
20+
Text="{Binding Test,Converter={StaticResource mockValueConverter}}" />
21+
</StackLayout>
22+
23+
</ContentPage>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Prism.DryIoc.Maui.Tests.Mocks.Views;
2+
3+
[XamlCompilation(XamlCompilationOptions.Compile)]
4+
public partial class MockXamlView : ContentPage
5+
{
6+
public MockXamlView()
7+
{
8+
InitializeComponent();
9+
}
10+
11+
public Entry TestEntry => testEntry;
12+
}

0 commit comments

Comments
 (0)