diff --git a/.github/workflows/build_forms.yml b/.github/workflows/build_forms.yml
index 3636add621..dd325f37e0 100644
--- a/.github/workflows/build_forms.yml
+++ b/.github/workflows/build_forms.yml
@@ -23,3 +23,4 @@ jobs:
with:
name: Build Prism.Forms
solution-path: PrismLibrary_Forms.slnf
+ jdk-version: 13
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 61f316f6f5..dc236c8f78 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,6 +51,7 @@ jobs:
solution-path: PrismLibrary_Forms.slnf
code-sign: true
artifact-name: Forms
+ jdk-version: 13
secrets:
codeSignKeyVault: ${{ secrets.CodeSignKeyVault }}
codeSignClientId: ${{ secrets.CodeSignClientId }}
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 436a97b645..7b11db7a98 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -12,27 +12,4 @@
$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
-
-
-
-
- $(IntermediateOutputPath)NotTrimmable.g.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/README.md b/README.md
index d0ae669a92..e5cfb7b104 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,10 @@
# Prism
-Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Xamarin Forms, Uno Platform and WinUI. Separate releases are available for each platform and those will be developed on independent timelines. Prism provides an implementation of a collection of design patterns that are helpful in writing well-structured and maintainable XAML applications, including MVVM, dependency injection, commands, EventAggregator, and others. Prism's core functionality is a shared code base supported in .NET Standard 2.0, .NET Framework 4.5 / 4.7. Those things that need to be platform specific are implemented in the respective libraries for the target platform. Prism also provides great integration of these patterns with the target platform. For example, Prism for Xamarin Forms allows you to use an abstraction for navigation that is unit testable, but that layers on top of the platform concepts and APIs for navigation so that you can fully leverage what the platform itself has to offer, but done in the MVVM way.
+Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Xamarin Forms, Uno Platform and WinUI. Separate releases are available for each platform and those will be developed on independent timelines. Prism provides an implementation of a collection of design patterns that are helpful in writing well-structured and maintainable XAML applications, including MVVM, dependency injection, commands, EventAggregator, and others. Prism's core functionality is a shared code base supported in .NET Standard 2.0, .NET Framework 4.6 / 4.7, and .NET6.0/.NET8.0. Those things that need to be platform specific are implemented in the respective libraries for the target platform. Prism also provides great integration of these patterns with the target platform.
-## Help Support Prism
+## Licensing
-As most of you know, it takes a lot of time and effort for our small team to manage and maintain Prism in our spare time. Even though Prism is open source and hosted on GitHub, there are a number of costs associated with maintaining a project such as Prism. Please be sure to Star the Prism repo and help sponsor Dan and Brian on GitHub. As a bonus GitHub sponsors get access to Sponsor Connect where you can access exclusive training content, all Prism CI builds, and a Sponsor Only Discord with Brian and Dan!
-
-Don't forget both Brian and Dan have content on YouTube and stream there from time to time. Be sure to subscribe to their channels and turn on notifications so you know when they do a Live Stream!
-
-| | Sponsor | Twitter | YouTube |
-|:-:|:--:|:--:|:--:|
-| Brian Lagunas | [![GitHub][OctoSponsor]](https://xam.dev/sponsor-prism-brian) | [![Twitter][TwitterLogo]](https://twitter.com/brianlagunas)
Follow | [![YouTube][YouTubeLogo]](https://youtube.com/brianlagunas)
Subcribe & Ring the Bell
-| Dan Siegel | [![GitHub][OctoSponsor]](https://xam.dev/sponsor-prism-dan) | [![Twitter][TwitterLogo]](https://twitter.com/DanJSiegel)
Follow | [![YouTube][YouTubeLogo]](https://youtube.com/dansiegel)
Subscribe & Ring the Bell
+The Prism Team would first and foremost like to thank all of those developers who have stepped up over the past 4 years with GitHub Sponsors. We are committed to ensuring the longevity and success of the Prism Library. As a result Prism 9.0 is now [Dual License](LICENSE). We continue to offer a FREE Community License for the vast majority of our community, while the Commercial License will now be required by larger organizations to help fund and support the development of Prism. We additionally have the Commercial Plus License which grants access to a number of additional support libraries that build on top of Prism as well as a private Discord channel where you can ask questions and interact with the Prism Team.
## Build Status
@@ -27,9 +20,10 @@ Don't forget both Brian and Dan have content on YouTube and stream there from ti
## Support
- Documentation is maintained in [the Prism-Documentation repo](https://github.com/PrismLibrary/Prism-Documentation) under /docs and can be found in a readable format on [the website](http://prismlibrary.com/docs/).
-- For general questions and support, post your questions on [StackOverflow](http://stackoverflow.com/questions/tagged/prism).
+- StackOverflow: **NOTE** The Prism Team no longer supports or engages with questions posted on StackOverflow. Questions posted there may or may not receive correct answers.
+- For general questions and support, post your questions in [GitHub Discussions](https://github.com/PrismLibrary/Prism/discussions).
- You can enter bugs and feature requests in our [Issues](https://github.com/PrismLibrary/Prism/issues/new/choose).
-- [Enterprise Support](https://avantipoint.com/contact?utm_source=github&utm_medium=prism-readme) is available exclusively from AvantiPoint, and helps to support this project.
+- Enterprise Support: If you are interested in Enterprise Support please email the Prism Team at
## Videos & Training
@@ -47,15 +41,19 @@ We appreciate your support.
## NuGet Packages
-Official Prism releases are available on NuGet. Prism packages are also available on the SponsorConnect feed which will be updated with each merged PR. If you want to take advantage of a new feature as soon as it's merged into the code base, or if there is a critical bug you need fixed we invite you to try the packages on this feed. The SponsorConnect package feed is available to Sponsors only.
+Official Prism releases are available on NuGet. Prism packages are also available on the private Commercial Plus Feed which will be updated with each merged PR. If you want to take advantage of a new feature as soon as it's merged into the code base, or if there is a critical bug you need fixed we invite you to purchase the Commercial Plus License to take advantage of these packages.
### Core Packages
These are the base packages for each platform, together with the Prism's Core assembly as a cross-platform PCL.
-| Platform | Package | NuGet | SponsorConnect |
+| Platform | Package | NuGet | Commercial Plus Feed |
| -------- | ------- | ------- | ----- |
| Cross Platform | [Prism.Core][CoreNuGet] | [![CoreNuGetShield]][CoreNuGet] | [![CoreSponsorConnectShield]][CoreSponsorConnect] |
+| Cross Platform | [Prism.Events][EventsNuGet] | [![EventsNuGetShield]][EventsNuGet] | [![CoreSponsorConnectShield]][CoreSponsorConnect] |
+| Cross Platform | [Prism.Container.Abstractions][ContainerAbstractionsNuGet] | [![ContainerAbstractionsNuGetShield]][ContainerAbstractionsNuGet] | [![CoreSponsorConnectShield]][CoreSponsorConnect] |
+| Cross Platform | [Prism.Container.DryIoc][ContainerDryIocNuGet] | [![ContainerDryIocNuGetShield]][ContainerDryIocNuGet] | [![CoreSponsorConnectShield]][CoreSponsorConnect] |
+| Cross Platform | [Prism.Container.Unity][ContainerUnityNuGet] | [![ContainerUnityNuGetShield]][ContainerUnityNuGet] | [![CoreSponsorConnectShield]][CoreSponsorConnect] |
| WPF | [Prism.Wpf][WpfNuGet] | [![WpfNuGetShield]][WpfNuGet] | [![WpfSponsorConnectShield]][WpfSponsorConnect] |
| Xamarin.Forms | [Prism.Forms][FormsNuGet] | [![FormsNuGetShield]][FormsNuGet] | [![FormsSponsorConnectShield]][FormsSponsorConnect] |
| Uno Platform and WinUI | [Prism.Uno][UnoNuGet] | [![UnoNuGetShield]][UnoNuGet] | [![UnoSponsorConnectShield]][UnoSponsorConnect] |
@@ -66,14 +64,14 @@ Each supported IoC container has its own package assisting in the setup and usag
#### WPF
-| Package | NuGet | SponsorConnect |
+| Package | NuGet | Commercial Plus Feed |
|---------|-------|-------|
| [Prism.DryIoc][DryIocWpfNuGet] | [![DryIocWpfNuGetShield]][DryIocWpfNuGet] | [![DryIocWpfSponsorConnectShield]][DryIocWpfSponsorConnect] |
| [Prism.Unity][UnityWpfNuGet] | [![UnityWpfNuGetShield]][UnityWpfNuGet] | [![UnityWpfSponsorConnectShield]][UnityWpfSponsorConnect] |
#### Xamarin Forms
-| Package | NuGet | SponsorConnect |
+| Package | NuGet | Commercial Plus Feed |
|---------|-------|-------|
| [Prism.DryIoc.Forms][DryIocFormsNuGet] | [![DryIocFormsNuGetShield]][DryIocFormsNuGet] | [![DryIocFormsSponsorConnectShield]][DryIocFormsSponsorConnect] |
| [Prism.Unity.Forms][UnityFormsNuGet] | [![UnityFormsNuGetShield]][UnityFormsNuGet] | [![UnityFormsSponsorConnectShield]][UnityFormsSponsorConnect] |
@@ -81,49 +79,14 @@ Each supported IoC container has its own package assisting in the setup and usag
#### Uno Platform
-| Package | NuGet | SponsorConnect |
+| Package | NuGet | Commercial Plus Feed |
|---------|-------|-------|
| [Prism.DryIoc.Uno][DryIocUnoPlatformNuGet] | [![DryIocUnoPlatformNuGetShield]][DryIocUnoPlatformNuGet] | [![DryIocUnoPlatformSponsorConnectShield]][DryIocUnoPlatformSponsorConnect] |
-| [Prism.Unity.Uno][UnityUnoPlatformNuGet] | [![UnityUnoPlatformNuGetShield]][UnityUnoPlatformNuGet] | [![UnityUnoPlatformSponsorConnectShield]][UnityUnoPlatformSponsorConnect] |
![NuGet package tree](images/NuGetPackageTree.png)
A detailed overview of each assembly per package is available [here](http://prismlibrary.github.io/docs/getting-started/NuGet-Packages.html).
-## Prism Template Pack
-
-Prism integrates with Visual Studio to enable a highly productive developer workflow for creating WPF, and native iOS and Android applications using Xamarin.Forms. Jump start your Prism apps with code snippets, item templates, and project templates for your IDE of choice.
-
-> **NOTE**
->
-> The Prism Templates are open source and available at
->
-> https://github.com/PrismLibrary/Prism.Templates
-
-### Visual Studio Gallery
-
-The Prism Template Pack is available on the [Visual Studio Gallery](https://marketplace.visualstudio.com/items?itemName=BrianLagunas.PrismTemplatePack). To install, just go to Visual Studio -> Tools -> Extensions and Updates... then search for **Prism** in the online gallery:
-
-![Visual Studio Gallery](images/prism-visual-studio-gallery.jpg)
-
-## Plugins
-
-There are certain things that cannot be added directly into Prism for various reasons. To handle these common tasks such as supporting PopupPage's in Xamarin Forms, there are Prism Plugins. You can find a number of Plugins available on NuGet from our maintainer [@DanJSiegel](https://twitter.com/DanJSiegel).
-
-- [Prism.Plugin.Popups](https://github.com/dansiegel/Prism.Plugin.Popups) (Forms Only)
-- [Prism.Popups.XCT](https://github.com/FileOnQ/Prism.Popups.XCT) (Forms Only)
- - Adds support for native popups using Xamarin Community Toolkits Popup API
-- [Prism.Plugin.Logging](https://github.com/dansiegel/Prism.Plugin.Logging) (Works on all Platforms)
- - Adds support for Syslog, Loggly, Graylog, Application Insights, & App Center
-- [Prism.Container.Extensions](https://github.com/dansiegel/Prism.Container.Extensions)
- - Adds advanced Container Registration abstractions
- - Adds DryIoc & Unity ContainerExtension with support for Microsoft.DependencyInjection.Extensions. Uses a singleton pattern to allow initialization from a native platform
- - Provides an extended PrismApplication with additional error handling and platform specifics support for Prism.Forms
-- [Prism.Magician](https://sponsorconnect.dev/nuget/package/prism.magician) (Works with ALL Platforms)
- - The Magician works to reduce the amount of code you need to write with a collection of intelligent code generators that evaluate your codebase and references
- - It additionally provides a series of Roslyn Analyzers to help prevent you from making common mistakes
- - **NOTE:** This package is only available to Dan's [GitHub Sponsors](https://xam.dev/sponsor-prism-dan) and [Enterprise Support](https://avantipoint.com/contact) customers.
-
## Samples
For stable samples be sure to check out the samples repo for the platform you are most interested in.
@@ -140,14 +103,18 @@ We strongly encourage you to get involved and help us evolve the code base.
- You can see what our expectations are for pull requests [here](https://github.com/PrismLibrary/Prism/blob/master/.github/CONTRIBUTING.md).
[CoreNuGet]: https://www.nuget.org/packages/Prism.Core/
+[EventsNuGet]: https://www.nuget.org/packages/Prism.Events/
+[ContainerAbstractionsNuGet]: https://www.nuget.org/packages/Prism.Container.Abstractions/
+[ContainerDryIocNuGet]: https://www.nuget.org/packages/Prism.Container.DryIoc/
+[ContainerUnityNuGet]: https://www.nuget.org/packages/Prism.Container.Unity/
[WpfNuGet]: https://www.nuget.org/packages/Prism.Wpf/
[FormsNuGet]: https://www.nuget.org/packages/Prism.Forms/
-[UnoNuGet]: https://www.nuget.org/packages/Prism.Uno/
+[UnoNuGet]: https://www.nuget.org/packages/Prism.Uno.WinUI/
[PrismFormsRegionsNuGet]: https://www.nuget.org/packages/Prism.Forms.Regions/
[PrismFormsRegionsSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Forms.Regions
[PrismFormsRegionsNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Forms.Regions.svg
-[PrismFormsRegionsSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Forms.Regions%2Fvpre
+[PrismFormsRegionsSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.Forms.Regions/vpre
[DryIocWpfNuGet]: https://www.nuget.org/packages/Prism.DryIoc/
[UnityWpfNuGet]: https://www.nuget.org/packages/Prism.Unity/
@@ -155,13 +122,16 @@ We strongly encourage you to get involved and help us evolve the code base.
[UnityFormsNuGet]: https://www.nuget.org/packages/Prism.Unity.Forms/
[DryIocFormsNuGet]: https://www.nuget.org/packages/Prism.DryIoc.Forms/
-[DryIocUnoPlatformNuGet]: https://www.nuget.org/packages/Prism.DryIoc.Uno/
-[UnityUnoPlatformNuGet]: https://www.nuget.org/packages/Prism.Unity.Uno/
+[DryIocUnoPlatformNuGet]: https://www.nuget.org/packages/Prism.DryIoc.Uno.WinUI/
[CoreNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Core.svg
+[EventsNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Events.svg
+[ContainerAbstractionsNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Container.Abstractions.svg
+[ContainerDryIocNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Container.DryIoc.svg
+[ContainerUnityNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Container.Unity.svg
[WpfNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Wpf.svg
[FormsNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Forms.svg
-[UnoNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Uno.svg
+[UnoNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Uno.WinUI.svg
[DryIocWpfNuGetShield]: https://img.shields.io/nuget/vpre/Prism.DryIoc.svg
[UnityWpfNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Unity.svg
@@ -169,13 +139,12 @@ We strongly encourage you to get involved and help us evolve the code base.
[DryIocFormsNuGetShield]: https://img.shields.io/nuget/vpre/Prism.DryIoc.Forms.svg
[UnityFormsNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Unity.Forms.svg
-[DryIocUnoPlatformNuGetShield]: https://img.shields.io/nuget/vpre/Prism.DryIoc.Uno.svg
-[UnityUnoPlatformNuGetShield]: https://img.shields.io/nuget/vpre/Prism.Unity.Uno.svg
+[DryIocUnoPlatformNuGetShield]: https://img.shields.io/nuget/vpre/Prism.DryIoc.Uno.WinUI.svg
[CoreSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Core
[WpfSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Wpf
[FormsSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Forms
-[UnoSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Uno
+[UnoSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Uno.WinUI
[DryIocWpfSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.DryIoc
[UnityWpfSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Unity
@@ -183,22 +152,20 @@ We strongly encourage you to get involved and help us evolve the code base.
[UnityFormsSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Unity.Forms
[DryIocFormsSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.DryIoc.Forms
-[DryIocUnoPlatformSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.DryIoc.Uno
-[UnityUnoPlatformSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.Unity.Uno
+[DryIocUnoPlatformSponsorConnect]: https://sponsorconnect.dev/nuget/package/Prism.DryIoc.Uno.WinUI
-[CoreSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Core%2Fvpre
-[WpfSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Wpf%2Fvpre
-[FormsSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Forms%2Fvpre
-[UnoSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Uno%2Fvpre
+[CoreSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.Core/vpre
+[WpfSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.Wpf/vpre
+[FormsSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.Forms/vpre
+[UnoSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.Uno.WinUI/vpre
-[DryIocWpfSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.DryIoc%2Fvpre
-[UnityWpfSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Unity%2Fvpre
+[DryIocWpfSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.DryIoc/vpre
+[UnityWpfSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.Unity/vpre
-[DryIocFormsSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.DryIoc.Forms%2Fvpre
-[UnityFormsSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Unity.Forms%2Fvpre
+[DryIocFormsSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.DryIoc.Forms/vpre
+[UnityFormsSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.Unity.Forms/vpre
-[DryIocUnoPlatformSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.DryIoc.Uno%2Fvpre
-[UnityUnoPlatformSponsorConnectShield]: https://img.shields.io/endpoint?url=https%3A%2F%2Fsponsorconnect.dev%2Fshield%2FPrism.Unity.Uno%2Fvpre
+[DryIocUnoPlatformSponsorConnectShield]: https://ci.nuget.prismlibrary.com/shield/Prism.DryIoc.Uno.WinUI/vpre
[TwitterLogo]: https://dansiegelgithubsponsors.blob.core.windows.net/images/twitter.png
[TwitchLogo]: https://dansiegelgithubsponsors.blob.core.windows.net/images/twitch.png
diff --git a/src/Forms/Prism.DryIoc.Forms/Prism.DryIoc.Forms.csproj b/src/Forms/Prism.DryIoc.Forms/Prism.DryIoc.Forms.csproj
index 329e0ec8ef..2dadcb429f 100644
--- a/src/Forms/Prism.DryIoc.Forms/Prism.DryIoc.Forms.csproj
+++ b/src/Forms/Prism.DryIoc.Forms/Prism.DryIoc.Forms.csproj
@@ -1,8 +1,6 @@
-
netstandard2.0
- $(TargetFrameworks);Xamarin.iOS10
DryIoc for Prism for Xamarin.Forms
@@ -17,5 +15,4 @@
-
diff --git a/src/Forms/Prism.Forms/Prism.Forms.csproj b/src/Forms/Prism.Forms/Prism.Forms.csproj
index 4c2a119b81..dd17c10235 100644
--- a/src/Forms/Prism.Forms/Prism.Forms.csproj
+++ b/src/Forms/Prism.Forms/Prism.Forms.csproj
@@ -3,7 +3,7 @@
Prism
netstandard2.0
- $(TargetFrameworks);MonoAndroid10.0
+ $(TargetFrameworks);MonoAndroid13.0
Prism for Xamarin.Forms
diff --git a/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs b/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs
index 86f02ed2a1..dc556f1285 100644
--- a/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs
+++ b/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs
@@ -25,11 +25,11 @@ public DialogContainerPage()
public ICommand Dismiss { get; private set; }
- public async Task ConfigureLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand)
+ public async Task ConfigureLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand, IDialogParameters parameters)
{
Dismiss = dismissCommand;
DialogView = dialogView;
- Content = GetContentLayout(currentPage, dialogView, hideOnBackgroundTapped, dismissCommand);
+ Content = GetContentLayout(currentPage, dialogView, hideOnBackgroundTapped, dismissCommand, parameters);
await DoPush(currentPage);
}
@@ -44,7 +44,7 @@ public virtual async Task DoPop(Page currentPage)
await currentPage.Navigation.PopModalAsync(false);
}
- protected virtual View GetContentLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand)
+ protected virtual View GetContentLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand, IDialogParameters parameters)
{
var overlay = new AbsoluteLayout();
var popupContainer = new DialogContainerView
diff --git a/src/Maui/Prism.Maui/Dialogs/DialogService.cs b/src/Maui/Prism.Maui/Dialogs/DialogService.cs
index c8335d8eda..5f10070d0a 100644
--- a/src/Maui/Prism.Maui/Dialogs/DialogService.cs
+++ b/src/Maui/Prism.Maui/Dialogs/DialogService.cs
@@ -91,7 +91,7 @@ async Task DialogAware_RequestClose(IDialogResult outResult)
var dismissCommand = new DelegateCommand(() => dialogAware.RequestClose.Invoke(), dialogAware.CanCloseDialog);
PageNavigationService.NavigationSource = PageNavigationSource.DialogService;
- dialogModal.ConfigureLayout(_pageAccessor.Page, view, closeOnBackgroundTapped, dismissCommand);
+ dialogModal.ConfigureLayout(_pageAccessor.Page, view, closeOnBackgroundTapped, dismissCommand, parameters);
PageNavigationService.NavigationSource = PageNavigationSource.Device;
MvvmHelpers.InvokeViewAndViewModelAction(currentPage, aa => aa.IsActive = false);
diff --git a/src/Maui/Prism.Maui/Dialogs/IDialogContainer.cs b/src/Maui/Prism.Maui/Dialogs/IDialogContainer.cs
index 5792d52e89..041e67ee3c 100644
--- a/src/Maui/Prism.Maui/Dialogs/IDialogContainer.cs
+++ b/src/Maui/Prism.Maui/Dialogs/IDialogContainer.cs
@@ -6,6 +6,6 @@ public interface IDialogContainer
{
View DialogView { get; }
ICommand Dismiss { get; }
- Task ConfigureLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand);
+ Task ConfigureLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand, IDialogParameters parameters);
Task DoPop(Page currentPage);
}
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/PageNavigationService.cs b/src/Maui/Prism.Maui/Navigation/PageNavigationService.cs
index e3aa9b89ce..dc8b57cb33 100644
--- a/src/Maui/Prism.Maui/Navigation/PageNavigationService.cs
+++ b/src/Maui/Prism.Maui/Navigation/PageNavigationService.cs
@@ -75,17 +75,15 @@ public PageNavigationService(IContainerProvider container,
public virtual async Task GoBackToAsync(string name, INavigationParameters parameters)
{
await _semaphore.WaitAsync();
- Page page = null;
try
{
parameters ??= new NavigationParameters();
NavigationSource = PageNavigationSource.NavigationService;
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException("No Navigation Key was specified to Navigate Back To");
- else if (!Registry.IsRegistered(name))
+ ArgumentException.ThrowIfNullOrEmpty(name);
+ if (!Registry.IsRegistered(name))
throw new NavigationException(NavigationException.NoPageIsRegistered, name);
- page = GetCurrentPage();
+ var page = GetCurrentPage();
parameters.GetNavigationParametersInternal().Add(KnownInternalParameters.NavigationMode, NavigationMode.Back);
while (page is not null && ViewModelLocator.GetNavigationName(page) != name)
@@ -99,11 +97,16 @@ public virtual async Task GoBackToAsync(string name, INavigat
throw new NavigationException(NavigationException.IConfirmNavigationReturnedFalse, page);
}
- bool useModalForDoPop = UseModalGoBack(page, parameters);
- Page previousPage = MvvmHelpers.GetOnNavigatedToTarget(page, Window?.Page, useModalForDoPop);
+ var useModalForDoPop = UseModalGoBack(page, parameters);
+ var previousPage = MvvmHelpers.GetOnNavigatedToTarget(page, Window?.Page, useModalForDoPop);
+
+ bool? animated = null;
+ if (!parameters.TryGetValue(KnownNavigationParameters.Animated, out var globalAnimated))
+ {
+ animated = true;
+ }
- bool animated = parameters.ContainsKey(KnownNavigationParameters.Animated) ? parameters.GetValue(KnownNavigationParameters.Animated) : true;
- var poppedPage = await DoPop(page.Navigation, useModalForDoPop, animated);
+ var poppedPage = await DoPop(page.Navigation, useModalForDoPop, animated ?? true);
if (poppedPage != null)
{
MvvmHelpers.OnNavigatedFrom(page, parameters);
@@ -135,6 +138,16 @@ public virtual async Task GoBackToAsync(string name, INavigat
public virtual async Task GoBackAsync(INavigationParameters parameters)
{
await _semaphore.WaitAsync();
+
+ INavigationResult result = await GoBackInternalAsync(parameters);
+
+ _semaphore.Release();
+
+ return result;
+ }
+
+ private async Task GoBackInternalAsync(INavigationParameters parameters)
+ {
Page page = null;
try
{
@@ -157,8 +170,8 @@ public virtual async Task GoBackAsync(INavigationParameters p
bool useModalForDoPop = UseModalGoBack(page, parameters);
Page previousPage = MvvmHelpers.GetOnNavigatedToTarget(page, Window?.Page, useModalForDoPop);
- bool animated = parameters.ContainsKey(KnownNavigationParameters.Animated) ? parameters.GetValue(KnownNavigationParameters.Animated) : true;
- var poppedPage = await DoPop(page.Navigation, useModalForDoPop, animated);
+ bool? animated = parameters.ContainsKey(KnownNavigationParameters.Animated) ? parameters.GetValue(KnownNavigationParameters.Animated) : null;
+ var poppedPage = await DoPop(page.Navigation, useModalForDoPop, animated ?? true);
if (poppedPage != null)
{
MvvmHelpers.OnNavigatedFrom(page, parameters);
@@ -175,7 +188,6 @@ public virtual async Task GoBackAsync(INavigationParameters p
finally
{
NavigationSource = PageNavigationSource.Device;
- _semaphore.Release();
}
return Notify(NavigationRequestType.GoBack, parameters, GetGoBackException(page, GetPageFromWindow()));
@@ -355,8 +367,7 @@ public virtual async Task NavigateAsync(Uri uri, INavigationP
try
{
- if (parameters is null)
- parameters = new NavigationParameters();
+ parameters ??= new NavigationParameters();
NavigationSource = PageNavigationSource.NavigationService;
@@ -364,11 +375,11 @@ public virtual async Task NavigateAsync(Uri uri, INavigationP
if (uri.IsAbsoluteUri)
{
- await ProcessNavigationForAbsoluteUri(navigationSegments, parameters, null, true);
+ await ProcessNavigationForAbsoluteUri(navigationSegments, parameters, null, null);
}
else
{
- await ProcessNavigation(GetCurrentPage(), navigationSegments, parameters, null, true);
+ await ProcessNavigation(GetCurrentPage(), navigationSegments, parameters, null, null);
}
return Notify(uri, parameters);
@@ -444,7 +455,7 @@ TabbedPage GetTabbedPage(Element page) =>
/// flag if we should force Modal Navigation.
/// If true, the navigation will be animated.
///
- protected virtual async Task ProcessNavigation(Page currentPage, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual async Task ProcessNavigation(Page currentPage, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
if (segments.Count == 0)
{
@@ -455,45 +466,46 @@ protected virtual async Task ProcessNavigation(Page currentPage, Queue s
var pageParameters = UriParsingHelper.GetSegmentParameters(nextSegment);
- useModalNavigation = pageParameters.ContainsKey(KnownNavigationParameters.UseModalNavigation) ? pageParameters.GetValue(KnownNavigationParameters.UseModalNavigation) : false;
- if (!useModalNavigation.Value && !MvvmHelpers.HasNavigationPageParent(currentPage) && (currentPage is not FlyoutPage || (currentPage.Parent is FlyoutPage parentFlyout && parentFlyout.Flyout == currentPage)))
- useModalNavigation = true;
+ if (pageParameters.TryGetValue(KnownNavigationParameters.UseModalNavigation, out var parameterModal))
+ useModalNavigation = parameterModal;
- animated = parameters.ContainsKey(KnownNavigationParameters.Animated) ?
- parameters.GetValue(KnownNavigationParameters.Animated) :
- pageParameters.ContainsKey(KnownNavigationParameters.Animated) ? pageParameters.GetValue(KnownNavigationParameters.Animated) : true;
+ bool? pageAnimation = animated;
+ if (animated is null && pageParameters.TryGetValue(KnownNavigationParameters.Animated, out var parameterAnimation))
+ {
+ pageAnimation = parameterAnimation;
+ }
if (nextSegment == RemovePageSegment)
{
- await ProcessNavigationForRemovePageSegments(currentPage, nextSegment, segments, parameters, useModalNavigation, animated);
+ await ProcessNavigationForRemovePageSegments(currentPage, nextSegment, segments, parameters, useModalNavigation, pageAnimation);
return;
}
if (currentPage is null)
{
- await ProcessNavigationForRootPage(nextSegment, segments, parameters, useModalNavigation, animated);
+ await ProcessNavigationForRootPage(nextSegment, segments, parameters, useModalNavigation, pageAnimation);
return;
}
if (currentPage is ContentPage)
{
- await ProcessNavigationForContentPage(currentPage, nextSegment, segments, parameters, useModalNavigation, animated);
+ await ProcessNavigationForContentPage(currentPage, nextSegment, segments, parameters, useModalNavigation, pageAnimation);
}
else if (currentPage is NavigationPage nav)
{
- await ProcessNavigationForNavigationPage(nav, nextSegment, segments, parameters, useModalNavigation, animated);
+ await ProcessNavigationForNavigationPage(nav, nextSegment, segments, parameters, useModalNavigation, pageAnimation);
}
else if (currentPage is TabbedPage tabbed)
{
- await ProcessNavigationForTabbedPage(tabbed, nextSegment, segments, parameters, useModalNavigation, animated);
+ await ProcessNavigationForTabbedPage(tabbed, nextSegment, segments, parameters, useModalNavigation, pageAnimation);
}
else if (currentPage is FlyoutPage flyout)
{
- await ProcessNavigationForFlyoutPage(flyout, nextSegment, segments, parameters, useModalNavigation, animated);
+ await ProcessNavigationForFlyoutPage(flyout, nextSegment, segments, parameters, useModalNavigation, pageAnimation);
}
}
- protected virtual Task ProcessNavigationForRemovePageSegments(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual Task ProcessNavigationForRemovePageSegments(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
if (!MvvmHelpers.HasDirectNavigationPageParent(currentPage))
{
@@ -510,7 +522,7 @@ private static bool CanRemoveAndPush(Queue segments)
return !segments.All(x => x == RemovePageSegment);
}
- private Task RemoveAndGoBack(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ private Task RemoveAndGoBack(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
var pagesToRemove = new List();
@@ -529,10 +541,10 @@ private Task RemoveAndGoBack(Page currentPage, string nextSegment, Queue
RemovePagesFromNavigationPage(currentPage, pagesToRemove);
- return GoBackAsync(parameters);
+ return GoBackInternalAsync(parameters);
}
- private async Task RemoveAndPush(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ private async Task RemoveAndPush(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
var pagesToRemove = new List
{
@@ -567,12 +579,12 @@ private static void RemovePagesFromNavigationPage(Page currentPage, List p
}
}
- protected virtual Task ProcessNavigationForAbsoluteUri(Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual Task ProcessNavigationForAbsoluteUri(Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
return ProcessNavigation(null, segments, parameters, useModalNavigation, animated);
}
- protected virtual async Task ProcessNavigationForRootPage(string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual async Task ProcessNavigationForRootPage(string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
var nextPage = CreatePageFromSegment(nextSegment);
if (nextPage is TabbedPage tabbedPage)
@@ -592,7 +604,7 @@ await DoNavigateAction(GetCurrentPage(), nextSegment, nextPage, parameters, asyn
}
}
- protected virtual async Task ProcessNavigationForContentPage(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual async Task ProcessNavigationForContentPage(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
var nextPageType = Registry.GetViewType(UriParsingHelper.GetSegmentName(nextSegment));
bool useReverse = UseReverseNavigation(currentPage, nextPageType) && !(useModalNavigation.HasValue && useModalNavigation.Value);
@@ -615,7 +627,7 @@ await DoNavigateAction(currentPage, nextSegment, nextPage, parameters, async ()
}
}
- protected virtual async Task ProcessNavigationForNavigationPage(NavigationPage currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual async Task ProcessNavigationForNavigationPage(NavigationPage currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
if (currentPage.Navigation.NavigationStack.Count == 0)
{
@@ -674,7 +686,7 @@ await DoNavigateAction(topPage, nextSegment, topPage, parameters, onNavigationAc
}
}
- protected virtual async Task ProcessNavigationForTabbedPage(TabbedPage currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual async Task ProcessNavigationForTabbedPage(TabbedPage currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
var nextPage = CreatePageFromSegment(nextSegment);
if (nextPage is TabbedPage tabbedPage)
@@ -686,7 +698,7 @@ await DoNavigateAction(currentPage, nextSegment, nextPage, parameters, async ()
});
}
- protected virtual async Task ProcessNavigationForFlyoutPage(FlyoutPage currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual async Task ProcessNavigationForFlyoutPage(FlyoutPage currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
bool isPresented = GetFlyoutPageIsPresented(currentPage);
@@ -1058,7 +1070,7 @@ private void TabbedPageSelectNavigationChildTab(TabbedPage tabbedPage, string ro
tabbedPage.CurrentPage = child;
}
- protected virtual async Task UseReverseNavigation(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool animated)
+ protected virtual async Task UseReverseNavigation(Page currentPage, string nextSegment, Queue segments, INavigationParameters parameters, bool? useModalNavigation, bool? animated)
{
var navigationStack = new Stack();
@@ -1134,16 +1146,14 @@ await DoNavigateAction(onNavigatedFromTarget, segment, nextPage, parameters, asy
await ProcessNavigation(currentPage.Navigation.NavigationStack.Last(), illegalSegments, parameters, true, animated);
}
- protected virtual async Task DoPush(Page currentPage, Page page, bool? useModalNavigation, bool animated, bool insertBeforeLast = false, int navigationOffset = 0)
+ protected virtual async Task DoPush(Page currentPage, Page page, bool? useModalNavigation, bool? animated, bool insertBeforeLast = false, int navigationOffset = 0)
{
- if (page is null)
- throw new ArgumentNullException(nameof(page));
+ ArgumentNullException.ThrowIfNull(page);
try
{
// Prevent Page from using Parent's ViewModel
- if (page.BindingContext is null)
- page.BindingContext = new object();
+ page.BindingContext ??= new object();
if (currentPage is null)
{
@@ -1170,7 +1180,7 @@ protected virtual async Task DoPush(Page currentPage, Page page, bool? useModalN
if (useModalForPush)
{
- await currentPage.Navigation.PushModalAsync(page, animated);
+ await currentPage.Navigation.PushModalAsync(page, animated ?? true);
}
else
{
@@ -1180,7 +1190,7 @@ protected virtual async Task DoPush(Page currentPage, Page page, bool? useModalN
}
else
{
- await currentPage.Navigation.PushAsync(page, animated);
+ await currentPage.Navigation.PushAsync(page, animated ?? true);
}
}
}
@@ -1213,8 +1223,7 @@ protected virtual Page GetCurrentPage()
internal static bool UseModalNavigation(Page currentPage, bool? useModalNavigationDefault)
{
- bool useModalNavigation = true;
-
+ bool useModalNavigation;
if (useModalNavigationDefault.HasValue)
useModalNavigation = useModalNavigationDefault.Value;
else if (currentPage is NavigationPage)
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/src/Maui/Prism.Maui/PrismAppBuilder.cs b/src/Maui/Prism.Maui/PrismAppBuilder.cs
index 1dcb36ddc9..2519894c02 100644
--- a/src/Maui/Prism.Maui/PrismAppBuilder.cs
+++ b/src/Maui/Prism.Maui/PrismAppBuilder.cs
@@ -1,3 +1,4 @@
+using Microsoft.Extensions.Logging;
using Microsoft.Maui.LifecycleEvents;
using Prism.AppModel;
using Prism.Behaviors;
@@ -16,11 +17,14 @@
namespace Prism;
+///
+/// A builder for Prism with .NET MAUI cross-platform applications and services.
+///
public sealed class PrismAppBuilder
{
- private List> _registrations { get; }
- private List> _initializations { get; }
- private IContainerProvider _container { get; }
+ private readonly List> _registrations;
+ private readonly List> _initializations;
+ private readonly IContainerProvider _container;
private Func _createWindow;
private Action _configureAdapters;
private Action _configureBehaviors;
@@ -152,12 +156,43 @@ internal void OnInitialized()
return;
_initialized = true;
- _initializations.ForEach(action => action(_container));
+ var logger = _container.Resolve>();
+ var errors = new List();
+
+ _initializations.ForEach(action =>
+ {
+ try
+ {
+ action(_container);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error executing Initialization Delegate.");
+ errors.Add(ex);
+ }
+ });
+
+ if (errors.Count == 1)
+ {
+ throw new PrismInitializationException("An error was encountered while invoking the OnInitialized Delegates", errors[0]);
+ }
+ else if (errors.Count > 1)
+ {
+ throw new AggregateException("One or more errors were encountered while executing the OnInitialized Delegates", [.. errors]);
+ }
if (_container.IsRegistered() && _container.Resolve().Modules.Any())
{
- var manager = _container.Resolve();
- manager.Run();
+ try
+ {
+ var manager = _container.Resolve();
+ manager.Run();
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "An error ocurred while initializing the Modules.");
+ throw new PrismInitializationException("An error occurred while initializing the Modules.", ex);
+ }
}
var navRegistry = _container.Resolve();
diff --git a/src/Maui/Prism.Maui/PrismInitializationException.cs b/src/Maui/Prism.Maui/PrismInitializationException.cs
new file mode 100644
index 0000000000..68b51534c6
--- /dev/null
+++ b/src/Maui/Prism.Maui/PrismInitializationException.cs
@@ -0,0 +1,18 @@
+namespace Prism;
+
+///
+/// Represents errors that occur during application initialization.
+///
+public sealed class PrismInitializationException : Exception
+{
+ ///
+ /// Initializes a new instance of the class with a specified error
+ /// message and a reference to the inner exception that is the cause of this exception.
+ ///
+ /// The error message that explains the reason for the exception.
+ /// The exception that is the cause of the current exception.
+ public PrismInitializationException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+}
diff --git a/src/Prism.Core/Common/IParameters.cs b/src/Prism.Core/Common/IParameters.cs
index 22025874c4..4fa05b4cc1 100644
--- a/src/Prism.Core/Common/IParameters.cs
+++ b/src/Prism.Core/Common/IParameters.cs
@@ -1,6 +1,5 @@
-using System;
using System.Collections.Generic;
-using System.Text;
+using System.Diagnostics.CodeAnalysis;
namespace Prism.Common
{
@@ -59,7 +58,7 @@ public interface IParameters : IEnumerable>
/// if the key is found; otherwise, the default value for the type of the value parameter.
///
/// true if the contains a parameter with the specified key; otherwise, false.
- bool TryGetValue(string key, out T value);
+ bool TryGetValue(string key, [MaybeNullWhen(false)] out T value);
///
/// Gets the parameter associated with the specified key (legacy).
diff --git a/src/Prism.Core/Common/IRegistryAware.cs b/src/Prism.Core/Common/IRegistryAware.cs
index 4639126cc4..b32f5d034b 100644
--- a/src/Prism.Core/Common/IRegistryAware.cs
+++ b/src/Prism.Core/Common/IRegistryAware.cs
@@ -2,7 +2,14 @@
namespace Prism.Common;
+///
+/// An internal marker API used within Prism to access the instance of the
+/// within a service where we do not want to publicly expose it but need access for Extension methods.
+///
public interface IRegistryAware
{
+ ///
+ /// The instance of the IViewRegistry
+ ///
IViewRegistry Registry { get; }
-}
\ No newline at end of file
+}
diff --git a/src/Prism.Core/Common/ListDictionary.cs b/src/Prism.Core/Common/ListDictionary.cs
index f55a0f757d..4c21b370e9 100644
--- a/src/Prism.Core/Common/ListDictionary.cs
+++ b/src/Prism.Core/Common/ListDictionary.cs
@@ -1,5 +1,3 @@
-
-
using System;
using System.Collections.Generic;
@@ -12,7 +10,7 @@ namespace Prism.Common
/// The type of the value held by lists.
public sealed class ListDictionary : IDictionary>
{
- Dictionary> innerValues = new Dictionary>();
+ readonly Dictionary> innerValues = [];
#region Public Methods
diff --git a/src/Prism.Core/Common/ParametersBase.cs b/src/Prism.Core/Common/ParametersBase.cs
index 2bbd7b23ef..077ff736af 100644
--- a/src/Prism.Core/Common/ParametersBase.cs
+++ b/src/Prism.Core/Common/ParametersBase.cs
@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
@@ -10,9 +11,9 @@ namespace Prism.Common
///
/// This is a generic parameters base class used for Dialog Parameters and Navigation Parameters.
///
- public abstract class ParametersBase : IParameters, IEnumerable>
+ public abstract class ParametersBase : IParameters
{
- private readonly List> _entries = new List>();
+ private readonly List> _entries = [];
///
/// Default constructor.
@@ -99,7 +100,7 @@ public object this[string key]
/// Returns an IEnumerable of the Keys in the collection.
///
public IEnumerable Keys =>
- _entries.Select(x => x.Key);
+ _entries.Select(x => x.Key).Distinct();
///
/// Adds the key and value to the parameters collection.
@@ -148,7 +149,7 @@ public IEnumerable GetValues(string key) =>
/// The type for the values to be returned.
/// The key for the value to be returned.
/// Value of the returned parameter if it exists.
- public bool TryGetValue(string key, out T value) =>
+ public bool TryGetValue(string key, [MaybeNullWhen(false)] out T value) =>
_entries.TryGetValue(key, out value);
IEnumerator IEnumerable.GetEnumerator() =>
@@ -180,7 +181,7 @@ public override string ToString()
queryBuilder.Append(Uri.EscapeDataString(kvp.Key));
queryBuilder.Append('=');
- queryBuilder.Append(Uri.EscapeDataString(kvp.Value != null ? kvp.Value.ToString() : ""));
+ queryBuilder.Append(Uri.EscapeDataString(kvp.Value?.ToString() is string str ? str : string.Empty));
}
}
diff --git a/src/Prism.Core/Common/ParametersExtensions.cs b/src/Prism.Core/Common/ParametersExtensions.cs
index 644da68c93..2f6f56d1e9 100644
--- a/src/Prism.Core/Common/ParametersExtensions.cs
+++ b/src/Prism.Core/Common/ParametersExtensions.cs
@@ -1,6 +1,7 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Prism.Common
@@ -64,9 +65,12 @@ public static bool TryGetValue(this IEnumerable>
{
if (string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0)
{
- var success = TryGetValueInternal(kvp, typeof(T), out object valueAsObject);
- value = (T)valueAsObject;
- return success;
+ var success = TryGetValueInternal(kvp, type, out var valueAsObject);
+ if (valueAsObject is T valueAsT)
+ {
+ value = valueAsT;
+ return success;
+ }
}
}
@@ -84,24 +88,26 @@ public static bool TryGetValue(this IEnumerable>
[EditorBrowsable(EditorBrowsableState.Never)]
public static IEnumerable GetValues(this IEnumerable> parameters, string key)
{
- List values = new List();
+ List values = [];
var type = typeof(T);
foreach (var kvp in parameters)
{
- if (string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0)
+ if (string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0 &&
+ TryGetValueInternal(kvp, type, out var value) &&
+ value is T valueAsT)
{
- TryGetValueInternal(kvp, type, out var value);
- values.Add((T)value);
+ values.Add(valueAsT);
}
}
- return values.AsEnumerable();
+ return values.ToArray();
}
private static bool TryGetValueInternal(KeyValuePair kvp, Type type, out object value)
{
value = GetDefault(type);
+ var valueAsString = kvp.Value is string str ? str : kvp.Value?.ToString();
var success = false;
if (kvp.Value == null)
{
@@ -117,9 +123,8 @@ private static bool TryGetValueInternal(KeyValuePair kvp, Type t
success = true;
value = kvp.Value;
}
- else if (type.IsEnum)
+ else if (type.IsEnum && !string.IsNullOrEmpty(valueAsString))
{
- var valueAsString = kvp.Value.ToString();
if (Enum.IsDefined(type, valueAsString))
{
success = true;
diff --git a/src/Prism.Core/Prism.Core.csproj b/src/Prism.Core/Prism.Core.csproj
index 16b36f91c6..4645786a66 100644
--- a/src/Prism.Core/Prism.Core.csproj
+++ b/src/Prism.Core/Prism.Core.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;net461;net47;net6.0
+ netstandard2.0;net462;net47;net6.0
Prism
Prism.Core
Prism
@@ -23,10 +23,6 @@
-
-
-
-
diff --git a/src/Prism.Events/Prism.Events.csproj b/src/Prism.Events/Prism.Events.csproj
index 5c4270a1f9..2411d17913 100644
--- a/src/Prism.Events/Prism.Events.csproj
+++ b/src/Prism.Events/Prism.Events.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;net461;net47;net6.0
+ netstandard2.0;net462;net47;net6.0
Prism.Events is a library that facilitates communication between loosely coupled components in an application. It provides an event aggregator service that allows publishers and subscribers to interact through events without direct references. With multicast publish/subscribe functionality, multiple publishers can raise the same event, and multiple subscribers can listen to it, enabling flexible and efficient communication.
diff --git a/src/Wpf/Prism.DryIoc.Wpf/Prism.DryIoc.Wpf.csproj b/src/Wpf/Prism.DryIoc.Wpf/Prism.DryIoc.Wpf.csproj
index 5b10a86ef7..3fdd5fdfe2 100644
--- a/src/Wpf/Prism.DryIoc.Wpf/Prism.DryIoc.Wpf.csproj
+++ b/src/Wpf/Prism.DryIoc.Wpf/Prism.DryIoc.Wpf.csproj
@@ -2,7 +2,7 @@
- net461;net47;net6.0-windows
+ net462;net47;net6.0-windows
true
Prism.DryIoc
Prism.DryIoc
diff --git a/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj b/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj
index d083b7e495..4e7f8a93e9 100644
--- a/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj
+++ b/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj
@@ -2,7 +2,7 @@
- net461;net47;net6.0-windows
+ net462;net47;net6.0-windows
true
Prism.Unity
Prism.Unity
diff --git a/src/Wpf/Prism.Wpf/Prism.Wpf.csproj b/src/Wpf/Prism.Wpf/Prism.Wpf.csproj
index 6f3d679b9c..3bb40f223a 100644
--- a/src/Wpf/Prism.Wpf/Prism.Wpf.csproj
+++ b/src/Wpf/Prism.Wpf/Prism.Wpf.csproj
@@ -4,7 +4,7 @@
Properties
Prism
- net461;net47;net6.0-windows
+ net462;net47;net6.0-windows
true
Prism provides an implementation of a collection of design patterns that are helpful in writing well structured, maintainable, and testable XAML applications, including MVVM, dependency injection, commanding, event aggregation, and more. Prism's core functionality is a shared library targeting the .NET Framework and .NET. Features that need to be platform specific are implemented in the respective libraries for the target platform (WPF, Uno Platform, .NET MAUI and Xamarin Forms).
diff --git a/tests/Forms/Prism.Forms.Tests/Navigation/NavigationParametersFixture.cs b/tests/Forms/Prism.Forms.Tests/Navigation/NavigationParametersFixture.cs
index e3afead9d5..cbd221ee2a 100644
--- a/tests/Forms/Prism.Forms.Tests/Navigation/NavigationParametersFixture.cs
+++ b/tests/Forms/Prism.Forms.Tests/Navigation/NavigationParametersFixture.cs
@@ -4,6 +4,7 @@
namespace Prism.Forms.Tests.Navigation
{
+ // TODO: This can probably be deleted as a duplicate. Determine if there are any non-duplicate tests.
public class NavigationParametersFixture
{
const string _uri = "?id=3&name=brian";
@@ -196,15 +197,16 @@ public void GetValuesReturnsArrayWhenParametersParsedFromQuery()
[Fact]
public void GetValuesReturnsArrayWhenNotUsingQuery()
{
- var parameters = new NavigationParameters();
- parameters.Add("id", new Person());
- parameters.Add("id", new Person());
- parameters.Add("id", null);
+ var parameters = new NavigationParameters
+ {
+ { "id", new Person() },
+ { "id", new Person() },
+ { "id", null }
+ };
var result = parameters.GetValues("id").ToArray();
- Assert.Equal(3, result.Count());
+ Assert.Equal(2, result.Length);
Assert.IsType(result[0]);
Assert.IsType(result[1]);
- Assert.Null(result[2]);
}
[Fact]
diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/DynamicTabbedPageNavigationFixture.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/DynamicTabbedPageNavigationFixture.cs
index 934cc23fcc..fe8a35da40 100644
--- a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/DynamicTabbedPageNavigationFixture.cs
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/DynamicTabbedPageNavigationFixture.cs
@@ -53,4 +53,38 @@ public void CreatesTabs_WithNavigationPageAndContentPage()
Assert.Same(tabbedPage.Children[0], tabbedPage.CurrentPage);
}
+
+ [Fact]
+ public async Task NavigatesModally_FromChild_OfNavigationPageTab()
+ {
+ var mauiApp = CreateBuilder(prism => prism.CreateWindow(navigation =>
+ navigation.CreateBuilder()
+ .AddTabbedSegment(t =>
+ t.CreateTab(ct => ct.AddNavigationPage().AddSegment("MockViewA"))
+ .CreateTab(ct => ct.AddNavigationPage().AddSegment("MockViewB")))
+ .NavigateAsync())).Build();
+ var window = GetWindow(mauiApp);
+ Assert.IsType(window.Page);
+ var tabbedPage = window.Page as TabbedPage;
+
+ Assert.IsType(tabbedPage.CurrentPage);
+ var navPage = (PrismNavigationPage)tabbedPage.CurrentPage;
+ Assert.Empty(navPage.Navigation.ModalStack);
+
+ var navService = Prism.Navigation.Xaml.Navigation.GetNavigationService(navPage.CurrentPage);
+ var result = await navService.CreateBuilder()
+ .AddSegment("MockViewC", useModalNavigation: true)
+ .NavigateAsync();
+
+ Assert.True(result.Success);
+
+ Assert.Single(navPage.Navigation.ModalStack);
+
+ var modalNavService = Prism.Navigation.Xaml.Navigation.GetNavigationService(navPage.Navigation.ModalStack[0]);
+
+ result = await modalNavService.NavigateAsync("MockViewD");
+ Assert.True(result.Success);
+
+ Assert.Equal(2, navPage.Navigation.ModalStack.Count);
+ }
}
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 52273ef6a1..7ff873c66e 100644
--- a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs
@@ -1,5 +1,6 @@
using Prism.Common;
using Prism.Controls;
+using Prism.DryIoc.Maui.Tests.Mocks.Navigation;
using Prism.DryIoc.Maui.Tests.Mocks.ViewModels;
using Prism.DryIoc.Maui.Tests.Mocks.Views;
using Prism.Navigation.Xaml;
@@ -39,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()
{
@@ -59,6 +77,12 @@ public async Task AddsPageFromRelativeURI()
Assert.IsType(rootPage.CurrentPage);
TestPage(rootPage.CurrentPage);
Assert.Equal(2, rootPage.Navigation.NavigationStack.Count);
+
+ var pushes = navService.GetPushes();
+ Assert.Single(pushes);
+ Assert.Equal(currentPage, pushes[0].CurrentPage);
+ Assert.Equal(rootPage.CurrentPage, pushes[0].Page);
+ Assert.Null(pushes[0].Animated);
}
[Fact]
@@ -83,6 +107,28 @@ public async Task RelativeNavigation_RemovesPage_AndNavigates()
Assert.Equal(2, rootPage.Navigation.NavigationStack.Count);
}
+ [Fact(Timeout = 5000)]
+ public async Task Issue3047_RelativeNavigation_RemovesPage_AndGoBack()
+ {
+ var mauiApp = CreateBuilder(prism => prism.CreateWindow("NavigationPage/MockViewA/MockViewB"))
+ .Build();
+ var window = GetWindow(mauiApp);
+
+ var rootPage = window.Page as NavigationPage;
+ Assert.NotNull(rootPage);
+ TestPage(rootPage);
+ var currentPage = rootPage.CurrentPage;
+ Assert.IsType(currentPage);
+ TestPage(currentPage);
+ var container = currentPage.GetContainerProvider();
+ var navService = container.Resolve();
+ Assert.Equal(2, rootPage.Navigation.NavigationStack.Count);
+ await navService.NavigateAsync("../");
+ Assert.IsType(rootPage.CurrentPage);
+ TestPage(rootPage.CurrentPage);
+ Assert.Single(rootPage.Navigation.NavigationStack);
+ }
+
[Fact]
public async Task AbsoluteNavigation_ResetsWindowPage()
{
@@ -375,7 +421,134 @@ public async Task NavigationPage_UsesRootPageTitle_WithTabbedParent()
Assert.Equal(MockViewA.ExpectedTitle, navPage.Title);
}
- private void TestPage(Page page)
+ [Fact]
+ public async Task Navigation_HasDefault_AnimatedIsNull()
+ {
+ var mauiApp = CreateBuilder(prism => prism
+ .CreateWindow(n => n.CreateBuilder()
+ .AddNavigationPage()
+ .AddSegment("MockViewA")))
+ .Build();
+ var window = GetWindow(mauiApp);
+ var navigationPage = (NavigationPage)window.Page;
+ var rootPage = navigationPage.RootPage;
+
+ var navigationService = Prism.Navigation.Xaml.Navigation.GetNavigationService(rootPage);
+ var pushes = navigationService.GetPushes();
+
+ Assert.Empty(pushes);
+
+ var result = await navigationService.NavigateAsync("MockViewB");
+
+ Assert.True(result.Success);
+ Assert.Single(pushes);
+ var push = pushes[0];
+
+ Assert.IsType(push.CurrentPage);
+ Assert.IsType(push.Page);
+
+ Assert.Equal(navigationPage.RootPage, push.CurrentPage);
+ Assert.Equal(navigationPage.CurrentPage, push.Page);
+
+ Assert.Null(push.Animated);
+ }
+
+ [Fact]
+ public async Task Navigation_Animation_IsTrue()
+ {
+ var mauiApp = CreateBuilder(prism => prism
+ .CreateWindow(n => n.CreateBuilder()
+ .AddNavigationPage()
+ .AddSegment("MockViewA")))
+ .Build();
+ var window = GetWindow(mauiApp);
+ var navigationPage = (NavigationPage)window.Page;
+ var rootPage = navigationPage.RootPage;
+
+ var navigationService = Prism.Navigation.Xaml.Navigation.GetNavigationService(rootPage);
+ var pushes = navigationService.GetPushes();
+
+ Assert.Empty(pushes);
+
+ var result = await navigationService.NavigateAsync($"MockViewB?{KnownNavigationParameters.Animated}=true");
+
+ Assert.True(result.Success);
+ Assert.Single(pushes);
+ var push = pushes[0];
+
+ Assert.IsType(push.CurrentPage);
+ Assert.IsType(push.Page);
+
+ Assert.Equal(navigationPage.RootPage, push.CurrentPage);
+ Assert.Equal(navigationPage.CurrentPage, push.Page);
+
+ Assert.True(push.Animated);
+ }
+
+ [Fact]
+ public async Task Navigation_Animation_IsFalse()
+ {
+ var mauiApp = CreateBuilder(prism => prism
+ .CreateWindow(n => n.CreateBuilder()
+ .AddNavigationPage()
+ .AddSegment("MockViewA")))
+ .Build();
+ var window = GetWindow(mauiApp);
+ var navigationPage = (NavigationPage)window.Page;
+ var rootPage = navigationPage.RootPage;
+
+ var navigationService = Prism.Navigation.Xaml.Navigation.GetNavigationService(rootPage);
+ var pushes = navigationService.GetPushes();
+
+ Assert.Empty(pushes);
+
+ var result = await navigationService.NavigateAsync($"MockViewB?{KnownNavigationParameters.Animated}=false");
+
+ Assert.True(result.Success);
+ Assert.Single(pushes);
+ var push = pushes[0];
+
+ Assert.IsType(push.CurrentPage);
+ Assert.IsType(push.Page);
+
+ Assert.Equal(navigationPage.RootPage, push.CurrentPage);
+ Assert.Equal(navigationPage.CurrentPage, push.Page);
+
+ Assert.False(push.Animated);
+ }
+
+ [Theory]
+ [InlineData("MockViewA", "MockViewB", null)]
+ [InlineData("NavigationPage/MockViewA", "MockViewB?useModalNavigation=true", true)]
+ public async Task PushesModally(string startUri, string requestUri, bool? expectedUseModal)
+ {
+ var mauiApp = CreateBuilder(prism => prism
+ .CreateWindow(n => n.NavigateAsync(startUri)))
+ .Build();
+ var window = GetWindow(mauiApp);
+ var page = window.Page;
+ if (page is NavigationPage navPage)
+ page = navPage.RootPage;
+
+ var navService = Prism.Navigation.Xaml.Navigation.GetNavigationService(page);
+
+ var result = await navService.NavigateAsync(requestUri);
+ Assert.True(result.Success);
+
+ var pushes = navService.GetPushes();
+ Assert.Single(pushes);
+ var push = pushes[0];
+
+ var parameters = UriParsingHelper.GetSegmentParameters(requestUri);
+ bool? useModalNavigation = null;
+ if (parameters.TryGetValue(KnownNavigationParameters.UseModalNavigation, out var parameterModal))
+ useModalNavigation = parameterModal;
+
+ Assert.Equal(expectedUseModal, push.UseModalNavigation);
+ Assert.True(PageNavigationService.UseModalNavigation(push.CurrentPage, useModalNavigation));
+ }
+
+ private static void TestPage(Page page)
{
Assert.NotNull(page.BindingContext);
if(page.Parent is not null)
diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/TestBase.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/TestBase.cs
index c7fe88e930..1f699e5958 100644
--- a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/TestBase.cs
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/TestBase.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Logging;
using Prism.DryIoc.Maui.Tests.Mocks;
using Prism.DryIoc.Maui.Tests.Mocks.Logging;
+using Prism.DryIoc.Maui.Tests.Mocks.Navigation;
using Prism.DryIoc.Maui.Tests.Mocks.ViewModels;
using Prism.DryIoc.Maui.Tests.Mocks.Views;
using Prism.Events;
@@ -26,6 +27,7 @@ protected MauiAppBuilder CreateBuilder(Action configurePrism)
{
prism.RegisterTypes(container =>
{
+ container.RegisterScoped();
container.RegisterForNavigation()
.RegisterForNavigation()
.RegisterForNavigation()
diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationPop.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationPop.cs
new file mode 100644
index 0000000000..70fc574589
--- /dev/null
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationPop.cs
@@ -0,0 +1,3 @@
+namespace Prism.DryIoc.Maui.Tests.Mocks.Navigation;
+
+public record NavigationPop(Page Page, bool UseModalNavigation, bool Animated);
diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationPush.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationPush.cs
new file mode 100644
index 0000000000..b664e36776
--- /dev/null
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationPush.cs
@@ -0,0 +1,3 @@
+namespace Prism.DryIoc.Maui.Tests.Mocks.Navigation;
+
+public record NavigationPush(Page CurrentPage, Page Page, bool? UseModalNavigation, bool? Animated, bool InsertBeforeLast, int NavigationOffset);
diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationTestRecorder.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationTestRecorder.cs
new file mode 100644
index 0000000000..5c3e5b9d73
--- /dev/null
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationTestRecorder.cs
@@ -0,0 +1,16 @@
+namespace Prism.DryIoc.Maui.Tests.Mocks.Navigation;
+
+internal class NavigationTestRecorder
+{
+ private readonly List _pops = [];
+ private readonly List _pushes = [];
+
+ public IReadOnlyList Pops => _pops;
+ public IReadOnlyList Pushes => _pushes;
+
+ public void Push(NavigationPush push) =>
+ _pushes.Add(push);
+
+ public void Pop(NavigationPop pop) =>
+ _pops.Add(pop);
+}
diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationTestRecorderExtensions.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationTestRecorderExtensions.cs
new file mode 100644
index 0000000000..fe6774eded
--- /dev/null
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/NavigationTestRecorderExtensions.cs
@@ -0,0 +1,20 @@
+namespace Prism.DryIoc.Maui.Tests.Mocks.Navigation;
+
+public static class NavigationTestRecorderExtensions
+{
+ public static IReadOnlyList GetPops(this INavigationService navigationService)
+ {
+ if (navigationService is not TestPageNavigationService testNav)
+ throw new InvalidCastException();
+
+ return testNav.Recorder.Pops;
+ }
+
+ public static IReadOnlyList GetPushes(this INavigationService navigationService)
+ {
+ if (navigationService is not TestPageNavigationService testNav)
+ throw new InvalidCastException();
+
+ return testNav.Recorder.Pushes;
+ }
+}
diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/TestPageNavigationService.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/TestPageNavigationService.cs
new file mode 100644
index 0000000000..13bc2e48c5
--- /dev/null
+++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Mocks/Navigation/TestPageNavigationService.cs
@@ -0,0 +1,33 @@
+using Prism.Common;
+using Prism.Events;
+
+namespace Prism.DryIoc.Maui.Tests.Mocks.Navigation;
+
+internal sealed class TestPageNavigationService : PageNavigationService
+{
+ public TestPageNavigationService(
+ IContainerProvider container,
+ IWindowManager windowManager,
+ IEventAggregator eventAggregator,
+ IPageAccessor pageAccessor,
+ NavigationTestRecorder recorder)
+ : base(container, windowManager, eventAggregator, pageAccessor)
+ {
+ Recorder = recorder;
+ }
+
+ public NavigationTestRecorder Recorder { get; }
+
+ protected override async Task DoPop(INavigation navigation, bool useModalNavigation, bool animated)
+ {
+ var page = await base.DoPop(navigation, useModalNavigation, animated);
+ Recorder.Pop(new NavigationPop(page, useModalNavigation, animated));
+ return page;
+ }
+
+ protected override Task DoPush(Page currentPage, Page page, bool? useModalNavigation, bool? animated, bool insertBeforeLast = false, int navigationOffset = 0)
+ {
+ Recorder.Push(new NavigationPush(currentPage, page, useModalNavigation, animated, insertBeforeLast, navigationOffset));
+ return base.DoPush(currentPage, page, useModalNavigation, animated, insertBeforeLast, navigationOffset);
+ }
+}
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);
+ }
+}
diff --git a/tests/Prism.Core.Tests/Navigation/NavigationParametersFixture.cs b/tests/Prism.Core.Tests/Navigation/NavigationParametersFixture.cs
index ce9c86aedd..d8d3a9cada 100644
--- a/tests/Prism.Core.Tests/Navigation/NavigationParametersFixture.cs
+++ b/tests/Prism.Core.Tests/Navigation/NavigationParametersFixture.cs
@@ -199,15 +199,16 @@ public void GetValuesReturnsArrayWhenParametersParsedFromQuery()
[Fact]
public void GetValuesReturnsArrayWhenNotUsingQuery()
{
- var parameters = new NavigationParameters();
- parameters.Add("id", new Person());
- parameters.Add("id", new Person());
- parameters.Add("id", null);
+ var parameters = new NavigationParameters
+ {
+ { "id", new Person() },
+ { "id", new Person() },
+ { "id", null } // null value should be ignored
+ };
var result = parameters.GetValues("id").ToArray();
- Assert.Equal(3, result.Count());
+ Assert.Equal(2, result.Count());
Assert.IsType(result[0]);
Assert.IsType(result[1]);
- Assert.Null(result[2]);
}
[Fact]