diff --git a/src/Maui/Prism.Maui/Mvvm/ViewModelLocator.cs b/src/Maui/Prism.Maui/Mvvm/ViewModelLocator.cs index 57cc71f72d..727e857f0d 100644 --- a/src/Maui/Prism.Maui/Mvvm/ViewModelLocator.cs +++ b/src/Maui/Prism.Maui/Mvvm/ViewModelLocator.cs @@ -1,4 +1,4 @@ -namespace Prism.Mvvm; +namespace Prism.Mvvm; /// /// This class defines the attached property and related change handler that calls the . @@ -9,7 +9,15 @@ public static class ViewModelLocator /// Instructs Prism whether or not to automatically create an instance of a ViewModel using a convention, and assign the associated View's to that instance. /// public static readonly BindableProperty AutowireViewModelProperty = - BindableProperty.CreateAttached("AutowireViewModel", typeof(ViewModelLocatorBehavior), typeof(ViewModelLocator), ViewModelLocatorBehavior.Automatic); + BindableProperty.CreateAttached("AutowireViewModel", typeof(ViewModelLocatorBehavior), typeof(ViewModelLocator), ViewModelLocatorBehavior.Automatic, propertyChanged: OnViewModelLocatorBehaviorChanged); + + private static void OnViewModelLocatorBehaviorChanged(BindableObject bindable, object oldValue, object newValue) + { + if (newValue is ViewModelLocatorBehavior behavior && behavior == ViewModelLocatorBehavior.Forced) + { + Autowire(bindable); + } + } internal static readonly BindableProperty ViewModelProperty = BindableProperty.CreateAttached("ViewModelType", diff --git a/src/Maui/Prism.Maui/Mvvm/ViewModelLocatorBehavior.cs b/src/Maui/Prism.Maui/Mvvm/ViewModelLocatorBehavior.cs index 9e04a3ca78..0c9ea79b7f 100644 --- a/src/Maui/Prism.Maui/Mvvm/ViewModelLocatorBehavior.cs +++ b/src/Maui/Prism.Maui/Mvvm/ViewModelLocatorBehavior.cs @@ -1,7 +1,33 @@ namespace Prism.Mvvm; +/// +/// Defines the behavior that the should use. +/// public enum ViewModelLocatorBehavior { + /// + /// The ViewModel will be lazily loaded by the Page/Region Navigation Services + /// or the DialogService. + /// + /// + /// This is the default and recommended value for the ViewModelLocator. This will + /// allow the View to be fully initialized and ensure that the proper ViewModel is + /// resolved based on the route name. + /// Automatic, - Disabled + + /// + /// This will disable Prism's automatic ViewModel Location + /// + Disabled, + + /// + /// This is not recommended for most situations + /// + /// + /// This is likely to cause breaks in the Container Scoping. It is recommended that + /// you allow Prism Page/Region Navigation Services or the Dialog Service properly + /// resolve the ViewModel. + /// + Forced } diff --git a/src/Maui/Prism.Maui/Navigation/Xaml/Navigation.cs b/src/Maui/Prism.Maui/Navigation/Xaml/Navigation.cs index 4952349be1..d4b264b6ff 100644 --- a/src/Maui/Prism.Maui/Navigation/Xaml/Navigation.cs +++ b/src/Maui/Prism.Maui/Navigation/Xaml/Navigation.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.ComponentModel; using Prism.Common; using Prism.Navigation.Internals; @@ -154,6 +154,15 @@ public static IContainerProvider GetContainerProvider(this BindableObject bindab { if (page.Parent is FlyoutPage flyout && flyout.Flyout == page) return flyout.GetContainerProvider(); + + if (Mvvm.ViewModelLocator.GetAutowireViewModel(page) == Mvvm.ViewModelLocatorBehavior.Forced) + { + container = ContainerLocator.Container.CreateScope(); + var accessor = container.Resolve(); + accessor.Page = page; + SetContainerProvider(page, container); + return container; + } } else if (bindable is Element element && element.Parent is not null) return GetContainerProvider(element.Parent); diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs index 657ede505a..7ff873c66e 100644 --- a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs @@ -40,6 +40,23 @@ public void PagesInjectScopedInstanceOfIPageAccessor(string uri) } } + [Fact] + public async Task ViewModelLocator_Forced_SetsContainer_ResolvedViewModel() + { + var mauiApp = CreateBuilder(prism => prism + .RegisterTypes(c => c.RegisterForNavigation()) + .CreateWindow("ForcedView")) + .Build(); + var window = GetWindow(mauiApp); + + Assert.IsType(window.Page); + Assert.IsType(window.Page.BindingContext); + + var viewModel = (ForcedViewModel)window.Page.BindingContext; + Assert.NotNull(viewModel.Page); + Assert.IsType(viewModel.Page); + } + [Fact] public async Task AddsPageFromRelativeURI() { diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ViewModels/ForcedViewModel.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ViewModels/ForcedViewModel.cs new file mode 100644 index 0000000000..7878eff78e --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/ViewModels/ForcedViewModel.cs @@ -0,0 +1,15 @@ +using Prism.Common; + +namespace Prism.DryIoc.Maui.Tests.Mocks.ViewModels; + +internal class ForcedViewModel +{ + public ForcedViewModel(IPageAccessor accessor) + { + _accessor = accessor; + } + + private readonly IPageAccessor _accessor; + + public Page Page => _accessor.Page; +} diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/ForcedView.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/ForcedView.cs new file mode 100644 index 0000000000..c606693188 --- /dev/null +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Views/ForcedView.cs @@ -0,0 +1,9 @@ +namespace Prism.DryIoc.Maui.Tests.Mocks.Views; + +internal class ForcedView : ContentPage +{ + public ForcedView() + { + ViewModelLocator.SetAutowireViewModel(this, ViewModelLocatorBehavior.Forced); + } +}