From 50c37738a6786577bb7ae59f2c59c4d88bfd666e Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 23 May 2022 12:15:18 -0700 Subject: [PATCH 01/11] use NavigationService for GoBack consistency --- .../NavigationPageSystemGoBackBehavior.cs | 1 + src/Prism.Maui/Common/PageUtilities.cs | 15 ++------------- src/Prism.Maui/PrismApplication.cs | 11 +++++++++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Prism.Maui/Behaviors/NavigationPageSystemGoBackBehavior.cs b/src/Prism.Maui/Behaviors/NavigationPageSystemGoBackBehavior.cs index 68015c9..ba29568 100644 --- a/src/Prism.Maui/Behaviors/NavigationPageSystemGoBackBehavior.cs +++ b/src/Prism.Maui/Behaviors/NavigationPageSystemGoBackBehavior.cs @@ -21,6 +21,7 @@ private void NavigationPage_Popped(object sender, NavigationEventArgs e) { if (PageNavigationService.NavigationSource == PageNavigationSource.Device) { + System.Diagnostics.Trace.WriteLine("NavigationPage has encountered an unhandled GoBack. Be sure to inherit from PrismNavigationPage."); PageUtilities.HandleSystemGoBack(e.Page, AssociatedObject.CurrentPage); } } diff --git a/src/Prism.Maui/Common/PageUtilities.cs b/src/Prism.Maui/Common/PageUtilities.cs index 122f048..7ae525c 100644 --- a/src/Prism.Maui/Common/PageUtilities.cs +++ b/src/Prism.Maui/Common/PageUtilities.cs @@ -229,19 +229,8 @@ public static void SetCurrentPageDelegate(Func getCurrentPageDelegat public static async Task HandleNavigationPageGoBack(NavigationPage navigationPage) { - var previousPage = navigationPage.CurrentPage; - var parameters = new NavigationParameters(); - parameters.GetNavigationParametersInternal().Add(KnownInternalParameters.NavigationMode, NavigationMode.Back); - if (!CanNavigate(previousPage, parameters) || - !await CanNavigateAsync(previousPage, parameters)) - return; - - PageNavigationService.NavigationSource = PageNavigationSource.NavigationService; - await navigationPage.PopAsync(); - PageNavigationService.NavigationSource = PageNavigationSource.Device; - OnNavigatedFrom(previousPage, parameters); - OnNavigatedTo(navigationPage.CurrentPage, parameters); - DestroyPage(previousPage); + var navigationService = Navigation.Xaml.Navigation.GetNavigationService(navigationPage); + await navigationService.GoBackAsync(); } public static void HandleSystemGoBack(IView previousPage, IView currentPage) diff --git a/src/Prism.Maui/PrismApplication.cs b/src/Prism.Maui/PrismApplication.cs index d0a0ca2..e7eb188 100644 --- a/src/Prism.Maui/PrismApplication.cs +++ b/src/Prism.Maui/PrismApplication.cs @@ -20,6 +20,17 @@ protected PrismApplication() _containerExtension = ContainerLocator.Current; RegisterTypes(_containerExtension); NavigationService = Container.Resolve((typeof(IApplication), this)); + this.ModalPopping += PrismApplication_ModalPopping; + } + + private async void PrismApplication_ModalPopping(object sender, ModalPoppingEventArgs e) + { + if (PageNavigationService.NavigationSource == PageNavigationSource.NavigationService) + return; + + e.Cancel = true; + var navService = Navigation.Xaml.Navigation.GetNavigationService(e.Modal); + await navService.GoBackAsync(); } void ILegacyPrismApplication.OnInitialized() => OnInitialized(); From 03c4a2493e93dab65d59d70eec90e0e8f29269b2 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 23 May 2022 12:17:25 -0700 Subject: [PATCH 02/11] add navigation request context --- src/Prism.Maui/Events/NavigationPubSubEvent.cs | 8 ++++++++ src/Prism.Maui/Navigation/NavigationRequestContext.cs | 10 ++++++++++ src/Prism.Maui/Navigation/NavigationRequestType.cs | 8 ++++++++ src/Prism.Maui/Navigation/PageNavigationService.cs | 5 ++++- 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/Prism.Maui/Events/NavigationPubSubEvent.cs create mode 100644 src/Prism.Maui/Navigation/NavigationRequestContext.cs create mode 100644 src/Prism.Maui/Navigation/NavigationRequestType.cs diff --git a/src/Prism.Maui/Events/NavigationPubSubEvent.cs b/src/Prism.Maui/Events/NavigationPubSubEvent.cs new file mode 100644 index 0000000..087d69a --- /dev/null +++ b/src/Prism.Maui/Events/NavigationPubSubEvent.cs @@ -0,0 +1,8 @@ +using Prism.Navigation; + +namespace Prism.Events; + +public class NavigationPubSubEvent : PubSubEvent +{ +} + diff --git a/src/Prism.Maui/Navigation/NavigationRequestContext.cs b/src/Prism.Maui/Navigation/NavigationRequestContext.cs new file mode 100644 index 0000000..7f6eb1c --- /dev/null +++ b/src/Prism.Maui/Navigation/NavigationRequestContext.cs @@ -0,0 +1,10 @@ +namespace Prism.Navigation; + +public record NavigationRequestContext +{ + public bool Cancelled => Result?.Exception is not null && Result.Exception is NavigationException ne && ne.Message == NavigationException.IConfirmNavigationReturnedFalse; + public NavigationRequestType Type { get; init; } + public Uri? Uri { get; init; } + public INavigationParameters Parameters { get; init; } + public INavigationResult Result { get; init; } +} \ No newline at end of file diff --git a/src/Prism.Maui/Navigation/NavigationRequestType.cs b/src/Prism.Maui/Navigation/NavigationRequestType.cs new file mode 100644 index 0000000..5cb9c61 --- /dev/null +++ b/src/Prism.Maui/Navigation/NavigationRequestType.cs @@ -0,0 +1,8 @@ +namespace Prism.Navigation; + +public enum NavigationRequestType +{ + Navigate, + GoBack, + GoToRoot, +} diff --git a/src/Prism.Maui/Navigation/PageNavigationService.cs b/src/Prism.Maui/Navigation/PageNavigationService.cs index ab63851..4b8af57 100644 --- a/src/Prism.Maui/Navigation/PageNavigationService.cs +++ b/src/Prism.Maui/Navigation/PageNavigationService.cs @@ -1,5 +1,6 @@ using Prism.Behaviors; using Prism.Common; +using Prism.Events; using Prism.Ioc; using Application = Microsoft.Maui.Controls.Application; @@ -19,6 +20,7 @@ public class PageNavigationService : INavigationService, IPageAware private readonly IContainerProvider _container; protected readonly IApplication _application; + protected readonly IEventAggregator _eventAggregator; protected Window Window; protected Page _page; @@ -48,10 +50,11 @@ Page IPageAware.Page /// The that will be used to resolve pages for navigation. /// The that will let us ensure the Application.MainPage is set. /// The that will apply base and custom behaviors to pages created in the . - public PageNavigationService(IContainerProvider container, IApplication application) + public PageNavigationService(IContainerProvider container, IApplication application, IEventAggregator eventAggregator) { _container = container; _application = application; + _eventAggregator = eventAggregator; } /// From 8163ca9d3695cf97561fadf4cafc8761f7388127 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 23 May 2022 12:20:35 -0700 Subject: [PATCH 03/11] add Animated parameter --- .../Navigation/KnownNavigationParameters.cs | 5 +++ .../Navigation/PageNavigationService.cs | 40 ++++++++++++------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Prism.Maui/Navigation/KnownNavigationParameters.cs b/src/Prism.Maui/Navigation/KnownNavigationParameters.cs index d24afd1..2c0cb05 100644 --- a/src/Prism.Maui/Navigation/KnownNavigationParameters.cs +++ b/src/Prism.Maui/Navigation/KnownNavigationParameters.cs @@ -17,6 +17,11 @@ public static class KnownNavigationParameters /// public const string UseModalNavigation = "useModalNavigation"; + /// + /// Used to control whether the navigation should be animated. + /// + public const string Animated = "animated"; + /// /// Used to define a navigation parameter that is bound directly to a CommandParameter via {Binding .}. /// diff --git a/src/Prism.Maui/Navigation/PageNavigationService.cs b/src/Prism.Maui/Navigation/PageNavigationService.cs index 4b8af57..69ad8ea 100644 --- a/src/Prism.Maui/Navigation/PageNavigationService.cs +++ b/src/Prism.Maui/Navigation/PageNavigationService.cs @@ -100,16 +100,18 @@ protected async virtual Task GoBackInternal(INavigationParame Page page = null; try { + if (parameters is null) + parameters = new NavigationParameters(); + NavigationSource = PageNavigationSource.NavigationService; page = GetCurrentPage(); if (IsRoot(GetPageFromWindow(), page)) throw new NavigationException(NavigationException.CannotPopApplicationMainPage, page); - var segmentParameters = UriParsingHelper.GetSegmentParameters(null, parameters); - segmentParameters.GetNavigationParametersInternal().Add(KnownInternalParameters.NavigationMode, NavigationMode.Back); + parameters.GetNavigationParametersInternal().Add(KnownInternalParameters.NavigationMode, NavigationMode.Back); - var canNavigate = await PageUtilities.CanNavigateAsync(page, segmentParameters); + var canNavigate = await PageUtilities.CanNavigateAsync(page, parameters); if (!canNavigate) { return new NavigationResult @@ -118,14 +120,15 @@ protected async virtual Task GoBackInternal(INavigationParame }; } - bool useModalForDoPop = UseModalGoBack(page, useModalNavigation); + bool useModalForDoPop = UseModalGoBack(page, parameters); Page previousPage = PageUtilities.GetOnNavigatedToTarget(page, Window?.Page, useModalForDoPop); + bool animated = parameters.ContainsKey(KnownNavigationParameters.Animated) ? parameters.GetValue(KnownNavigationParameters.Animated) : true; var poppedPage = await DoPop(page.Navigation, useModalForDoPop, animated); if (poppedPage != null) { - PageUtilities.OnNavigatedFrom(page, segmentParameters); - PageUtilities.OnNavigatedTo(previousPage, segmentParameters); + PageUtilities.OnNavigatedFrom(page, parameters); + PageUtilities.OnNavigatedTo(previousPage, parameters); PageUtilities.DestroyPage(poppedPage); return new NavigationResult { Success = true }; @@ -226,7 +229,10 @@ protected async virtual Task GoBackToRootInternal(INavigation var root = pagesToDestroy.Last(); pagesToDestroy.Remove(root); //don't destroy the root page - await page.Navigation.PopToRootAsync(); + bool animated = parameters.ContainsKey(KnownNavigationParameters.Animated) ? parameters.GetValue(KnownNavigationParameters.Animated) : true; + NavigationSource = PageNavigationSource.NavigationService; + await page.Navigation.PopToRootAsync(animated); + NavigationSource = PageNavigationSource.Device; foreach (var destroyPage in pagesToDestroy) { @@ -242,6 +248,10 @@ protected async virtual Task GoBackToRootInternal(INavigation { return new NavigationResult { Exception = ex }; } + finally + { + NavigationSource = PageNavigationSource.Device; + } } /// @@ -290,6 +300,9 @@ protected async virtual Task NavigateInternal(Uri uri, INavig { try { + if (parameters is null) + parameters = new NavigationParameters(); + NavigationSource = PageNavigationSource.NavigationService; var navigationSegments = UriParsingHelper.GetUriSegments(uri); @@ -334,10 +347,9 @@ protected virtual async Task ProcessNavigation(Page currentPage, Queue s var nextSegment = segments.Dequeue(); var pageParameters = UriParsingHelper.GetSegmentParameters(nextSegment); - if (pageParameters.ContainsKey(KnownNavigationParameters.UseModalNavigation)) - { - useModalNavigation = pageParameters.GetValue(KnownNavigationParameters.UseModalNavigation); - } + var useModalNavigation = pageParameters.ContainsKey(KnownNavigationParameters.UseModalNavigation) ? pageParameters.GetValue(KnownNavigationParameters.UseModalNavigation) : false; + + var animated = pageParameters.ContainsKey(KnownNavigationParameters.Animated) ? pageParameters.GetValue(KnownNavigationParameters.Animated) : true; if (nextSegment == RemovePageSegment) { @@ -1043,10 +1055,10 @@ internal static bool UseModalNavigation(Page currentPage, bool? useModalNavigati return useModalNavigation; } - internal bool UseModalGoBack(Page currentPage, bool? useModalNavigationDefault) + internal bool UseModalGoBack(Page currentPage, INavigationParameters parameters) { - if (useModalNavigationDefault.HasValue) - return useModalNavigationDefault.Value; + if (parameters.ContainsKey(KnownNavigationParameters.UseModalNavigation)) + return parameters.GetValue(KnownNavigationParameters.UseModalNavigation); else if (currentPage is NavigationPage navPage) return GoBackModal(navPage); else if (PageUtilities.HasNavigationPageParent(currentPage, out var navParent)) From c23a6994916406f3bef1eb2ea96462b0de162b18 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 23 May 2022 12:23:24 -0700 Subject: [PATCH 04/11] Clean up GoBack method calls --- .../Navigation/PageNavigationService.cs | 47 +------------------ 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/src/Prism.Maui/Navigation/PageNavigationService.cs b/src/Prism.Maui/Navigation/PageNavigationService.cs index 69ad8ea..b87fae5 100644 --- a/src/Prism.Maui/Navigation/PageNavigationService.cs +++ b/src/Prism.Maui/Navigation/PageNavigationService.cs @@ -57,45 +57,12 @@ public PageNavigationService(IContainerProvider container, IApplication applicat _eventAggregator = eventAggregator; } - /// - /// Navigates to the most recent entry in the back navigation history by popping the calling Page off the navigation stack. - /// - /// If true a go back operation was successful. If false the go back operation failed. - public virtual Task GoBackAsync() - { - return GoBackAsync(null); - } - /// /// Navigates to the most recent entry in the back navigation history by popping the calling Page off the navigation stack. /// /// The navigation parameters /// If true a go back operation was successful. If false the go back operation failed. - public virtual Task GoBackAsync(INavigationParameters parameters) - { - return GoBackInternal(parameters, null, true); - } - - /// - /// Navigates to the most recent entry in the back navigation history by popping the calling Page off the navigation stack. - /// - /// The navigation parameters - /// If true uses PopModalAsync, if false uses PopAsync - /// If true the transition is animated, if false there is no animation on transition. - /// indicating whether the request was successful or if there was an encountered . - public virtual Task GoBackAsync(INavigationParameters parameters, bool? useModalNavigation, bool animated) - { - return GoBackInternal(parameters, useModalNavigation, animated); - } - - /// - /// Navigates to the most recent entry in the back navigation history by popping the calling Page off the navigation stack. - /// - /// The navigation parameters - /// If true uses PopModalAsync, if false uses PopAsync - /// If true the transition is animated, if false there is no animation on transition. - /// If true a go back operation was successful. If false the go back operation failed. - protected async virtual Task GoBackInternal(INavigationParameters parameters, bool? useModalNavigation, bool animated) + public virtual async Task GoBackAsync(INavigationParameters parameters) { Page page = null; try @@ -195,17 +162,7 @@ private static bool IsMainPage(IView currentPage, IView mainPage) /// The navigation parameters /// indicating whether the request was successful or if there was an encountered . /// Only works when called from a View within a NavigationPage - public virtual Task GoBackToRootAsync(INavigationParameters parameters) - { - return GoBackToRootInternal(parameters); - } - - /// - /// When navigating inside a NavigationPage: Pops all but the root Page off the navigation stack - /// - /// The navigation parameters - /// Only works when called from a View within a NavigationPage - protected async virtual Task GoBackToRootInternal(INavigationParameters parameters) + public virtual async Task GoBackToRootAsync(INavigationParameters parameters) { try { From 08923f44ee4b7f857e3c638cd8c4fb6c2115cdad Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 23 May 2022 13:05:35 -0700 Subject: [PATCH 05/11] Cleaning up Navigation Calls --- .../Events/NavigationPubSubEvent.cs | 8 ---- .../Events/NavigationRequestEvent.cs | 8 ++++ .../Navigation/PageNavigationService.cs | 45 +++---------------- 3 files changed, 14 insertions(+), 47 deletions(-) delete mode 100644 src/Prism.Maui/Events/NavigationPubSubEvent.cs create mode 100644 src/Prism.Maui/Events/NavigationRequestEvent.cs diff --git a/src/Prism.Maui/Events/NavigationPubSubEvent.cs b/src/Prism.Maui/Events/NavigationPubSubEvent.cs deleted file mode 100644 index 087d69a..0000000 --- a/src/Prism.Maui/Events/NavigationPubSubEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Prism.Navigation; - -namespace Prism.Events; - -public class NavigationPubSubEvent : PubSubEvent -{ -} - diff --git a/src/Prism.Maui/Events/NavigationRequestEvent.cs b/src/Prism.Maui/Events/NavigationRequestEvent.cs new file mode 100644 index 0000000..23c591e --- /dev/null +++ b/src/Prism.Maui/Events/NavigationRequestEvent.cs @@ -0,0 +1,8 @@ +using Prism.Navigation; + +namespace Prism.Events; + +public class NavigationRequestEvent : PubSubEvent +{ +} + diff --git a/src/Prism.Maui/Navigation/PageNavigationService.cs b/src/Prism.Maui/Navigation/PageNavigationService.cs index b87fae5..b3f2d91 100644 --- a/src/Prism.Maui/Navigation/PageNavigationService.cs +++ b/src/Prism.Maui/Navigation/PageNavigationService.cs @@ -49,7 +49,7 @@ Page IPageAware.Page /// /// The that will be used to resolve pages for navigation. /// The that will let us ensure the Application.MainPage is set. - /// The that will apply base and custom behaviors to pages created in the . + /// The that will raise . public PageNavigationService(IContainerProvider container, IApplication application, IEventAggregator eventAggregator) { _container = container; @@ -211,23 +211,6 @@ public virtual async Task GoBackToRootAsync(INavigationParame } } - /// - /// Initiates navigation to the target specified by the . - /// - /// The name of the target to navigate to. - /// The navigation parameters - /// If true uses PopModalAsync, if false uses PopAsync - /// If true the transition is animated, if false there is no animation on transition. - protected virtual Task NavigateInternal(string name, INavigationParameters parameters, bool? useModalNavigation, bool animated) - { - if (name.StartsWith(RemovePageRelativePath)) - { - name = name.Replace(RemovePageRelativePath, RemovePageInstruction); - } - - return NavigateInternal(UriParsingHelper.Parse(name), parameters, useModalNavigation, animated); - } - /// /// Initiates navigation to the target specified by the . /// @@ -237,23 +220,7 @@ protected virtual Task NavigateInternal(string name, INavigat /// /// NavigateAsync(new Uri("MainPage?id=3&name=brian", UriKind.RelativeSource), parameters); /// - public virtual Task NavigateAsync(Uri uri, INavigationParameters parameters) - { - return NavigateInternal(uri, parameters, null, true); - } - - /// - /// Initiates navigation to the target specified by the . - /// - /// The Uri to navigate to - /// The navigation parameters - /// If true uses PopModalAsync, if false uses PopAsync - /// If true the transition is animated, if false there is no animation on transition. - /// Navigation parameters can be provided in the Uri and by using the . - /// - /// Navigate(new Uri("MainPage?id=3&name=brian", UriKind.RelativeSource), parameters); - /// - protected async virtual Task NavigateInternal(Uri uri, INavigationParameters parameters, bool? useModalNavigation, bool animated) + public virtual async Task NavigateAsync(Uri uri, INavigationParameters parameters) { try { @@ -266,12 +233,12 @@ protected async virtual Task NavigateInternal(Uri uri, INavig if (uri.IsAbsoluteUri) { - await ProcessNavigationForAbsoluteUri(navigationSegments, parameters, useModalNavigation, animated); + await ProcessNavigationForAbsoluteUri(navigationSegments, parameters, null, true); return new NavigationResult { Success = true }; } else { - await ProcessNavigation(GetCurrentPage(), navigationSegments, parameters, useModalNavigation, animated); + await ProcessNavigation(GetCurrentPage(), navigationSegments, parameters, null, true); return new NavigationResult { Success = true }; } } @@ -304,9 +271,9 @@ protected virtual async Task ProcessNavigation(Page currentPage, Queue s var nextSegment = segments.Dequeue(); var pageParameters = UriParsingHelper.GetSegmentParameters(nextSegment); - var useModalNavigation = pageParameters.ContainsKey(KnownNavigationParameters.UseModalNavigation) ? pageParameters.GetValue(KnownNavigationParameters.UseModalNavigation) : false; + //var useModalNavigation = pageParameters.ContainsKey(KnownNavigationParameters.UseModalNavigation) ? pageParameters.GetValue(KnownNavigationParameters.UseModalNavigation) : false; - var animated = pageParameters.ContainsKey(KnownNavigationParameters.Animated) ? pageParameters.GetValue(KnownNavigationParameters.Animated) : true; + //var animated = pageParameters.ContainsKey(KnownNavigationParameters.Animated) ? pageParameters.GetValue(KnownNavigationParameters.Animated) : true; if (nextSegment == RemovePageSegment) { From c9a4771d113d1fa4bb3856a02493af5bae9aa148 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 23 May 2022 13:28:37 -0700 Subject: [PATCH 06/11] raise Navigation Request Notifications --- .../Navigation/PageNavigationService.cs | 65 +++++++++++++------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/src/Prism.Maui/Navigation/PageNavigationService.cs b/src/Prism.Maui/Navigation/PageNavigationService.cs index b3f2d91..2420bcc 100644 --- a/src/Prism.Maui/Navigation/PageNavigationService.cs +++ b/src/Prism.Maui/Navigation/PageNavigationService.cs @@ -81,10 +81,7 @@ public virtual async Task GoBackAsync(INavigationParameters p var canNavigate = await PageUtilities.CanNavigateAsync(page, parameters); if (!canNavigate) { - return new NavigationResult - { - Exception = new NavigationException(NavigationException.IConfirmNavigationReturnedFalse, page) - }; + throw new NavigationException(NavigationException.IConfirmNavigationReturnedFalse, page); } bool useModalForDoPop = UseModalGoBack(page, parameters); @@ -98,22 +95,19 @@ public virtual async Task GoBackAsync(INavigationParameters p PageUtilities.OnNavigatedTo(previousPage, parameters); PageUtilities.DestroyPage(poppedPage); - return new NavigationResult { Success = true }; + return Notify(NavigationRequestType.GoBack, parameters); } } catch (Exception ex) { - return new NavigationResult { Exception = ex }; + return Notify(NavigationRequestType.GoBack, parameters, ex); } finally { NavigationSource = PageNavigationSource.Device; } - return new NavigationResult - { - Exception = GetGoBackException(page, GetPageFromWindow()) - }; + return Notify(NavigationRequestType.GoBack, parameters, GetGoBackException(page, GetPageFromWindow())); } private static Exception GetGoBackException(Page currentPage, IView mainPage) @@ -175,10 +169,7 @@ public virtual async Task GoBackToRootAsync(INavigationParame var canNavigate = await PageUtilities.CanNavigateAsync(page, parameters); if (!canNavigate) { - return new NavigationResult - { - Exception = new NavigationException(NavigationException.IConfirmNavigationReturnedFalse, page) - }; + throw new NavigationException(NavigationException.IConfirmNavigationReturnedFalse, page); } var pagesToDestroy = page.Navigation.NavigationStack.ToList(); // get all pages to destroy @@ -199,11 +190,11 @@ public virtual async Task GoBackToRootAsync(INavigationParame PageUtilities.OnNavigatedTo(root, parameters); - return new NavigationResult { Success = true }; + return Notify(NavigationRequestType.GoToRoot, parameters); } catch (Exception ex) { - return new NavigationResult { Exception = ex }; + return Notify(NavigationRequestType.GoToRoot, parameters, ex); } finally { @@ -234,17 +225,17 @@ public virtual async Task NavigateAsync(Uri uri, INavigationP if (uri.IsAbsoluteUri) { await ProcessNavigationForAbsoluteUri(navigationSegments, parameters, null, true); - return new NavigationResult { Success = true }; } else { await ProcessNavigation(GetCurrentPage(), navigationSegments, parameters, null, true); - return new NavigationResult { Success = true }; } + + return Notify(uri, parameters); } catch (Exception ex) { - return new NavigationResult { Exception = ex }; + return Notify(uri, parameters, ex); } finally { @@ -1009,6 +1000,42 @@ internal static bool UseReverseNavigation(Page currentPage, Type nextPageType) return PageUtilities.HasNavigationPageParent(currentPage) && PageUtilities.IsSameOrSubclassOf(nextPageType); } + private INavigationResult Notify(NavigationRequestType type, INavigationParameters parameters, Exception exception = null) + { + var result = new NavigationResult + { + Exception = exception, + Success = exception is null + }; + _eventAggregator.GetEvent().Publish(new NavigationRequestContext + { + Parameters = parameters, + Result = result, + Type = type, + }); + + return result; + } + + private INavigationResult Notify(Uri uri, INavigationParameters parameters, Exception exception = null) + { + var result = new NavigationResult + { + Exception = exception, + Success = exception is null, + }; + + _eventAggregator.GetEvent().Publish(new NavigationRequestContext + { + Parameters = parameters, + Result = result, + Type = NavigationRequestType.Navigate, + Uri = uri, + }); + + return result; + } + protected static bool IsRoot(Page mainPage, Page currentPage) { if (mainPage == currentPage) From e2c038bbd1ed6bf3bd7ad1c62d234bc69ae3e4ca Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 23 May 2022 13:47:29 -0700 Subject: [PATCH 07/11] Add Observables Package --- Prism.Maui.sln | 21 +++++++++++++- src/Prism.Maui.Rx/GlobalNavigationObserver.cs | 29 +++++++++++++++++++ .../IGlobalNavigationObserver.cs | 8 +++++ ...avigationObserverRegistrationExtensions.cs | 17 +++++++++++ src/Prism.Maui.Rx/Prism.Maui.Rx.csproj | 15 ++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/Prism.Maui.Rx/GlobalNavigationObserver.cs create mode 100644 src/Prism.Maui.Rx/IGlobalNavigationObserver.cs create mode 100644 src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs create mode 100644 src/Prism.Maui.Rx/Prism.Maui.Rx.csproj diff --git a/Prism.Maui.sln b/Prism.Maui.sln index a5650f1..ed67a84 100644 --- a/Prism.Maui.sln +++ b/Prism.Maui.sln @@ -9,7 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6CAB3327-8C0 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.DryIoc.Maui", "src\Prism.DryIoc.Maui\Prism.DryIoc.Maui.csproj", "{6C5C1F1F-DA92-4307-862F-5034BF8B0858}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Maui", "src\Prism.Maui\Prism.Maui.csproj", "{6171FE41-1F11-4CFA-8FD8-8222B97C7C63}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prism.Maui", "src\Prism.Maui\Prism.Maui.csproj", "{6171FE41-1F11-4CFA-8FD8-8222B97C7C63}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Prism.DryIoc.Shared", "src\Prism.DryIoc.Shared\Prism.DryIoc.Shared.shproj", "{6E7EC81D-DA39-4C4F-A898-0148558C34F4}" EndProject @@ -30,6 +30,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiModule", "sample\MauiModule\MauiModule.csproj", "{EB8CD219-0CA7-4AE3-A7B7-6D883400CCE0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prism.Maui.Rx", "src\Prism.Maui.Rx\Prism.Maui.Rx.csproj", "{D364B3F0-2D41-4EF8-BE1D-D83825322F61}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Prism.DryIoc.Shared\Prism.DryIoc.Shared.projitems*{6c5c1f1f-da92-4307-862f-5034bf8b0858}*SharedItemsImports = 5 @@ -127,6 +129,22 @@ Global {EB8CD219-0CA7-4AE3-A7B7-6D883400CCE0}.Release|x64.Build.0 = Release|Any CPU {EB8CD219-0CA7-4AE3-A7B7-6D883400CCE0}.Release|x86.ActiveCfg = Release|Any CPU {EB8CD219-0CA7-4AE3-A7B7-6D883400CCE0}.Release|x86.Build.0 = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|arm64.ActiveCfg = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|arm64.Build.0 = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|x64.ActiveCfg = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|x64.Build.0 = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|x86.ActiveCfg = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Debug|x86.Build.0 = Debug|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|Any CPU.Build.0 = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|arm64.ActiveCfg = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|arm64.Build.0 = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|x64.ActiveCfg = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|x64.Build.0 = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|x86.ActiveCfg = Release|Any CPU + {D364B3F0-2D41-4EF8-BE1D-D83825322F61}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -138,6 +156,7 @@ Global {3BFAB205-050B-47B7-9AB8-BE799E07DA90} = {7CB47440-CCBC-4E16-B2B9-9FD472154117} {16AEEF3D-B119-4EE0-9B1F-B9E0FDF0EEAB} = {06F06128-578F-449D-B3D6-D04A531B1018} {EB8CD219-0CA7-4AE3-A7B7-6D883400CCE0} = {06F06128-578F-449D-B3D6-D04A531B1018} + {D364B3F0-2D41-4EF8-BE1D-D83825322F61} = {6CAB3327-8C0A-4D0B-BF18-CEFD15B84BA7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7E696A15-8B47-4BC2-8997-071E61157406} diff --git a/src/Prism.Maui.Rx/GlobalNavigationObserver.cs b/src/Prism.Maui.Rx/GlobalNavigationObserver.cs new file mode 100644 index 0000000..befd906 --- /dev/null +++ b/src/Prism.Maui.Rx/GlobalNavigationObserver.cs @@ -0,0 +1,29 @@ +using System; +using System.Reactive.Subjects; +using Prism.Events; + +namespace Prism.Navigation; + +internal class GlobalNavigationObserver : IGlobalNavigationObserver, IDisposable +{ + private readonly Subject _subject; + private SubscriptionToken _token; + + public GlobalNavigationObserver(IEventAggregator eventAggregator) + { + _subject = new Subject(); + _token = eventAggregator.GetEvent().Subscribe(context => _subject.OnNext(context)); + } + + public IObservable NavigationRequest => _subject; + + public void Dispose() + { + if (_token is null) + return; + + _token.Dispose(); + _token = null; + _subject.Dispose(); + } +} diff --git a/src/Prism.Maui.Rx/IGlobalNavigationObserver.cs b/src/Prism.Maui.Rx/IGlobalNavigationObserver.cs new file mode 100644 index 0000000..1474089 --- /dev/null +++ b/src/Prism.Maui.Rx/IGlobalNavigationObserver.cs @@ -0,0 +1,8 @@ +using System; + +namespace Prism.Navigation; + +public interface IGlobalNavigationObserver +{ + IObservable NavigationRequest { get; } +} diff --git a/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs b/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs new file mode 100644 index 0000000..13a38e6 --- /dev/null +++ b/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs @@ -0,0 +1,17 @@ +using Prism.Ioc; + +namespace Prism.Navigation; + +public static class NavigationObserverRegistrationExtensions +{ + private static bool s_IsRegistered; + + public static IContainerRegistry RegisterGlobalNavigationObserver(this IContainerRegistry container) + { + if (s_IsRegistered) + return container; + + s_IsRegistered = true; + return container.RegisterSingleton(); + } +} \ No newline at end of file diff --git a/src/Prism.Maui.Rx/Prism.Maui.Rx.csproj b/src/Prism.Maui.Rx/Prism.Maui.Rx.csproj new file mode 100644 index 0000000..fe0f4c5 --- /dev/null +++ b/src/Prism.Maui.Rx/Prism.Maui.Rx.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + + + + + + + + + + + From efbba3c0f2a22b6407f62cf81e96d5286d4bd492 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Tue, 24 May 2022 09:13:53 -0700 Subject: [PATCH 08/11] fix go back --- src/Prism.Maui/Common/PageUtilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Prism.Maui/Common/PageUtilities.cs b/src/Prism.Maui/Common/PageUtilities.cs index 7ae525c..4ea1190 100644 --- a/src/Prism.Maui/Common/PageUtilities.cs +++ b/src/Prism.Maui/Common/PageUtilities.cs @@ -229,7 +229,7 @@ public static void SetCurrentPageDelegate(Func getCurrentPageDelegat public static async Task HandleNavigationPageGoBack(NavigationPage navigationPage) { - var navigationService = Navigation.Xaml.Navigation.GetNavigationService(navigationPage); + var navigationService = Navigation.Xaml.Navigation.GetNavigationService(navigationPage.CurrentPage); await navigationService.GoBackAsync(); } From 12b6638838054b3b1aa3b4ac0930a4dbe6450ddc Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Tue, 24 May 2022 09:39:54 -0700 Subject: [PATCH 09/11] update package descriptions --- Directory.Build.props | 8 +++++--- src/Prism.DryIoc.Maui/Prism.DryIoc.Maui.csproj | 1 + src/Prism.Maui.Rx/Prism.Maui.Rx.csproj | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d7fdad5..f7d8373 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,9 +16,11 @@ $(MSBuildThisFileDirectory)LICENSE prism;maui;dotnet-maui;xaml;mvvm;ios;android;mac;winui Copyright Dan Siegel 2021 - This is a special preview package. In order to use this be sure that you have installed the Maui workload with Visual Studio 2022 preview 4 or later. Alternatively you may use Maui-Check to ensure that your environment has the required prerequesites to build from the command line with Visual Studio Code. + + Prism is a fully open source version of the Prism guidance originally produced by Microsoft Patterns & Practices. 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 Standard. Features that need to be platform specific are implemented in the respective libraries for the target platform (WPF, Uno Platform, Xamarin Forms, and .NET MAUI). -As this is special preview build there are no docs, be sure to check out the source repo at: https://github.com/dansiegel/prism.maui + Prism for .NET MAUI helps you more easily design and build rich, flexible, and easy to maintain .NET MAUI applications. This library provides user interface composition as well as modularity support. + true false @@ -38,7 +40,7 @@ As this is special preview build there are no docs, be sure to check out the sou true true true + Prism.DryIoc.Maui provides the implementation of Prism's IContainerExtension using the DryIoc container. This is currently the only supported container for .NET MAUI. diff --git a/src/Prism.Maui.Rx/Prism.Maui.Rx.csproj b/src/Prism.Maui.Rx/Prism.Maui.Rx.csproj index fe0f4c5..65145f8 100644 --- a/src/Prism.Maui.Rx/Prism.Maui.Rx.csproj +++ b/src/Prism.Maui.Rx/Prism.Maui.Rx.csproj @@ -2,6 +2,8 @@ net6.0 + true + Prism.Maui.Rx is a support package for .NET MAUI developers. This package provides some helpers to access an IObservable for globally handling Navigation Request Results. From 465eff3a9c81b9197f35a05869980c13492c2e02 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Tue, 24 May 2022 09:58:06 -0700 Subject: [PATCH 10/11] add helper methods --- ...NavigationObserverRegistrationExtensions.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs b/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs index 13a38e6..638195d 100644 --- a/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs +++ b/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs @@ -1,4 +1,5 @@ -using Prism.Ioc; +using Microsoft.Extensions.DependencyInjection; +using Prism.Ioc; namespace Prism.Navigation; @@ -14,4 +15,19 @@ public static IContainerRegistry RegisterGlobalNavigationObserver(this IContaine s_IsRegistered = true; return container.RegisterSingleton(); } + + public static IServiceCollection RegisterGlobalNavigationObserver(this IServiceCollection services) + { + if (s_IsRegistered) + return services; + + s_IsRegistered = true; + return services.AddSingleton(); + } + + public static PrismAppBuilder AddGlobalNavigationObserver(this PrismAppBuilder builder, Action> addObservable) => + builder.OnInitialized(c => addObservable(c.Resolve().NavigationRequest)); + + public static PrismAppBuilder AddGlobalNavigationObserver(this PrismAppBuilder builder, Action> addObservable) => + builder.OnInitialized(c => addObservable(c, c.Resolve().NavigationRequest)); } \ No newline at end of file From f0df704b4aa8fdd917f2339de80f0d828230a3d6 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Tue, 24 May 2022 10:07:37 -0700 Subject: [PATCH 11/11] add Navigation Observer to sample --- sample/PrismMauiDemo/MauiProgram.cs | 11 +++++++++++ sample/PrismMauiDemo/PrismMauiDemo.csproj | 1 + .../NavigationObserverRegistrationExtensions.cs | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/sample/PrismMauiDemo/MauiProgram.cs b/sample/PrismMauiDemo/MauiProgram.cs index 274297c..cedef67 100644 --- a/sample/PrismMauiDemo/MauiProgram.cs +++ b/sample/PrismMauiDemo/MauiProgram.cs @@ -16,10 +16,21 @@ public static MauiApp CreateMauiApp() }) .RegisterTypes(containerRegistry => { + containerRegistry.RegisterGlobalNavigationObserver(); containerRegistry.RegisterForNavigation(); containerRegistry.RegisterForNavigation(); containerRegistry.RegisterForNavigation(); }) + .AddGlobalNavigationObserver(context => context.Subscribe(x => + { + if (x.Type == NavigationRequestType.Navigate) + Console.WriteLine($"Navigation: {x.Type} - {x.Uri}"); + else + Console.WriteLine($"Navigation: {x.Type}"); + + var status = x.Cancelled ? "Cancelled" : x.Result.Success ? "Success" : "Failed"; + Console.WriteLine($"Result: {status}"); + })) .OnAppStart(navigationService => navigationService.CreateBuilder() .AddNavigationSegment("MainPage") .AddNavigationPage() diff --git a/sample/PrismMauiDemo/PrismMauiDemo.csproj b/sample/PrismMauiDemo/PrismMauiDemo.csproj index 77d7b35..a81774e 100644 --- a/sample/PrismMauiDemo/PrismMauiDemo.csproj +++ b/sample/PrismMauiDemo/PrismMauiDemo.csproj @@ -51,6 +51,7 @@ + diff --git a/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs b/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs index 638195d..f34a157 100644 --- a/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs +++ b/src/Prism.Maui.Rx/NavigationObserverRegistrationExtensions.cs @@ -26,8 +26,20 @@ public static IServiceCollection RegisterGlobalNavigationObserver(this IServiceC } public static PrismAppBuilder AddGlobalNavigationObserver(this PrismAppBuilder builder, Action> addObservable) => - builder.OnInitialized(c => addObservable(c.Resolve().NavigationRequest)); + builder.OnInitialized(c => + { + if (!s_IsRegistered) + throw new Exception("IGlobalNavigationObserver has not been registered. Be sure to call 'container.RegisterGlobalNavigationObserver()'."); + + addObservable(c.Resolve().NavigationRequest); + }); public static PrismAppBuilder AddGlobalNavigationObserver(this PrismAppBuilder builder, Action> addObservable) => - builder.OnInitialized(c => addObservable(c, c.Resolve().NavigationRequest)); + builder.OnInitialized(c => + { + if (!s_IsRegistered) + throw new Exception("IGlobalNavigationObserver has not been registered. Be sure to call 'container.RegisterGlobalNavigationObserver()'."); + + addObservable(c, c.Resolve().NavigationRequest); + }); } \ No newline at end of file