diff --git a/src/Maui/Prism.Maui/Ioc/ContainerProvider.cs b/src/Maui/Prism.Maui/Ioc/ContainerProvider.cs new file mode 100644 index 000000000..c2c25858f --- /dev/null +++ b/src/Maui/Prism.Maui/Ioc/ContainerProvider.cs @@ -0,0 +1,67 @@ +#nullable enable +namespace Prism.IoC; + +/// +/// Provides Types and Services registered with the Container +/// +/// The type to Resolve +/// +/// We can use this to build better types such as ValueConverters with full dependency injection +/// +/// public class MyValueConverter : IValueConverter +/// { +/// private readonly ILogger _logger { get; } +/// +/// public MyValueConverter(ILogger logger) +/// { +/// _logger = logger; +/// } +/// +/// public object Convert(object value, Type targetType, object parameter, CultureInfo culture) +/// { +/// _logger.Log($"Converting {value.GetType().Name} to {targetType.Name}", Category.Debug, Priority.None); +/// // do stuff +/// } +/// +/// public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) +/// { +/// _logger.Log($"Converting back from {value.GetType().Name} to {targetType.Name}", Category.Debug, Priority.None); +/// return null; +/// } +/// } +/// +/// We can then simply use our ValueConverter or other class directly in XAML +/// +/// +/// +/// +/// +/// +/// +public class ContainerProvider +{ + /// + /// The Name used to register the type with the Container + /// + public string? Name { get; set; } + + /// + /// Resolves the specified type from the Application's Container + /// + /// + public static implicit operator T(ContainerProvider containerProvider) + { + var container = ContainerLocator.Container; + if (container == null) return default(T); + if (string.IsNullOrWhiteSpace(containerProvider.Name)) + { + return container.Resolve(); + } + + return container.Resolve(containerProvider.Name); + } +} diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/IoC/ContainerProviderTests.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/IoC/ContainerProviderTests.cs new file mode 100644 index 000000000..2188cf3f9 --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/IoC/ContainerProviderTests.cs @@ -0,0 +1,76 @@ +using Prism.DryIoc.Maui.Tests.Mocks; +using Prism.DryIoc.Maui.Tests.Mocks.Events; +using Prism.DryIoc.Maui.Tests.Mocks.ViewModels; +using Prism.DryIoc.Maui.Tests.Mocks.Views; +using Prism.Events; +using Prism.IoC; + +namespace Prism.DryIoc.Maui.Tests.Fixtures.IoC; + +public class ContainerProviderTests(ITestOutputHelper testOutputHelper) : TestBase(testOutputHelper) +{ + [Fact] + public void CanResolveUnamedType() + { + var builder = CreateBuilder(prism => { }); + var app = builder.Build(); + var containerProvider = new ContainerProvider(); + + ConcreteTypeMock type = (ConcreteTypeMock)containerProvider; + + Assert.NotNull(type); + Assert.IsType(type); + } + + [Fact] + public void CanResolvedNamedType() + { + var builder = CreateBuilder(prism => { }); + var app = builder.Build(); + var containerProvider = new ContainerProvider + { + Name = ConcreteTypeMock.Key + }; + + ConcreteTypeMock vm = (ConcreteTypeMock)containerProvider; + + Assert.NotNull(vm); + Assert.IsType(vm); + } + + [Fact] + public async Task ProvidesValueFromResourceDictionary() + { + var builder = CreateBuilder(prism => + { + prism.RegisterTypes(containerRegistry => + { + containerRegistry.RegisterForNavigation(); + }) + .CreateWindow(n => + n.CreateBuilder() + .AddSegment() + .NavigateAsync()); + }); + var app = builder.Build(); + + var ea = app.Services.GetService(); + var events = new List(); + ea.GetEvent().Subscribe((m) => events.Add(m)); + + var navigation = app.Services.GetService(); + await navigation.CreateBuilder() + .AddSegment() + .NavigateAsync(); + + Assert.Contains(events, e => e == "Convert"); + var window = GetWindow(app); + Assert.NotNull(window.Page); + + var xamlView = window.Page as MockXamlView; + var viewModel = xamlView.BindingContext as MockXamlViewViewModel; + + xamlView.TestEntry.Text = "Foo Bar"; + Assert.Contains(events, e => e == "ConvertBack"); + } +} diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ConcreteTypeMock.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ConcreteTypeMock.cs new file mode 100644 index 000000000..82957cb47 --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ConcreteTypeMock.cs @@ -0,0 +1,6 @@ +namespace Prism.DryIoc.Maui.Tests.Mocks; + +internal class ConcreteTypeMock +{ + public const string Key = "ConcreteTypeMock"; +} diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Converters/MockValueConverter.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Converters/MockValueConverter.cs new file mode 100644 index 000000000..c1d0a4609 --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Converters/MockValueConverter.cs @@ -0,0 +1,27 @@ +using System.Globalization; +using Prism.DryIoc.Maui.Tests.Mocks.Events; +using Prism.Events; + +namespace Prism.DryIoc.Maui.Tests.Mocks.Converters; + +internal class MockValueConverter : IValueConverter +{ + private IEventAggregator _eventAggreator { get; } + + public MockValueConverter(IEventAggregator eventAggreator) + { + _eventAggreator = eventAggreator; + } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + _eventAggreator.GetEvent().Publish("Convert"); + return value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + _eventAggreator.GetEvent().Publish("ConvertBack"); + return value; + } +} diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Events/TestActionEvent.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Events/TestActionEvent.cs new file mode 100644 index 000000000..b0450f26d --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Events/TestActionEvent.cs @@ -0,0 +1,7 @@ +using Prism.Events; + +namespace Prism.DryIoc.Maui.Tests.Mocks.Events; + +internal class TestActionEvent : PubSubEvent +{ +} diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ViewModels/MockXamlViewViewModel.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ViewModels/MockXamlViewViewModel.cs new file mode 100644 index 000000000..da95836cd --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ViewModels/MockXamlViewViewModel.cs @@ -0,0 +1,11 @@ +namespace Prism.DryIoc.Maui.Tests.Mocks.ViewModels; + +internal class MockXamlViewViewModel : BindableBase +{ + private string _test = "Initial Value"; + public string Test + { + get => _test; + set => SetProperty(ref _test, value); + } +} diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/MockXamlView.xaml b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/MockXamlView.xaml new file mode 100644 index 000000000..63552a756 --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/MockXamlView.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/MockXamlView.xaml.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/MockXamlView.xaml.cs new file mode 100644 index 000000000..c5aa846e8 --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/MockXamlView.xaml.cs @@ -0,0 +1,12 @@ +namespace Prism.DryIoc.Maui.Tests.Mocks.Views; + +[XamlCompilation(XamlCompilationOptions.Compile)] +public partial class MockXamlView : ContentPage +{ + public MockXamlView() + { + InitializeComponent(); + } + + public Entry TestEntry => testEntry; +}