From 8356889b6cf9c837edf98533474c057e5b161601 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 19 May 2022 14:00:58 -0500 Subject: [PATCH] Setup WinUI to run window scoped tests in new window (#7308) * Setup WinUI to run window scoped tests in new window * - fix csproj file * - fix namespaces * - fix namespaces --- .../FlyoutPage/FlyoutPageTests.Windows.cs | 1 + .../Elements/FlyoutPage/FlyoutPageTests.cs | 1 + .../DeviceTests/Elements/Modal/ModalTests.cs | 1 + .../NavigationPageTests.Android.cs | 1 + .../NavigationPage/NavigationPageTests.cs | 1 + .../TabbedPage/TabbedPageTests.Windows.cs | 1 + .../Elements/Window/WindowTests.cs | 1 + .../DeviceTests/HandlerTestBase.Android.cs | 15 +--- .../DeviceTests/HandlerTestBase.Windows.cs | 72 +++++-------------- .../tests/DeviceTests/HandlerTestBase.cs | 41 +++++++++-- .../tests/DeviceTests/HandlerTestBase.iOS.cs | 21 ++---- .../DeviceTests/Stubs/MauiAppNewWindowStub.cs | 41 +++++++++++ .../Stubs/WindowHandlerStub.Android.cs | 2 +- .../Stubs/WindowHandlerStub.Windows.cs | 42 ++--------- .../Stubs/WindowHandlerStub.iOS.cs | 2 +- .../Handlers/Window/WindowHandler.Windows.cs | 3 +- .../Windows/StackNavigationManager.cs | 20 +++++- 17 files changed, 132 insertions(+), 134 deletions(-) create mode 100644 src/Controls/tests/DeviceTests/Stubs/MauiAppNewWindowStub.cs diff --git a/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.Windows.cs index f4fd4f8be78c..df14f2001411 100644 --- a/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.Windows.cs @@ -15,6 +15,7 @@ using Microsoft.Maui.Hosting; using Microsoft.Maui.Handlers; using Microsoft.UI.Xaml.Controls; +using Microsoft.Maui.DeviceTests.Stubs; namespace Microsoft.Maui.DeviceTests { diff --git a/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.cs b/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.cs index 0b1241c1b6cb..814556eb9c76 100644 --- a/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/FlyoutPage/FlyoutPageTests.cs @@ -7,6 +7,7 @@ using Microsoft.Maui; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Handlers; +using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; using Microsoft.Maui.Platform; diff --git a/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.cs b/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.cs index 80ea2b5ed651..56011ad732ae 100644 --- a/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Maui.Controls; +using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; using Xunit; diff --git a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.Android.cs index 10d2b5a39784..03a5d26edcaa 100644 --- a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.Android.cs @@ -7,6 +7,7 @@ using Microsoft.Maui; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Handlers; +using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Platform; using Xunit; diff --git a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs index 18884a4e41b9..81519efbcf4d 100644 --- a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Maui.Controls; +using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; using Xunit; diff --git a/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Windows.cs index ccca63dc03c5..9e7e325d52ee 100644 --- a/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Windows.cs @@ -13,6 +13,7 @@ using Microsoft.Maui.Hosting; using Microsoft.Maui.Handlers; using Microsoft.Maui.Graphics; +using Microsoft.Maui.DeviceTests.Stubs; namespace Microsoft.Maui.DeviceTests { diff --git a/src/Controls/tests/DeviceTests/Elements/Window/WindowTests.cs b/src/Controls/tests/DeviceTests/Elements/Window/WindowTests.cs index 8ef717f0ff06..14a5f7decb55 100644 --- a/src/Controls/tests/DeviceTests/Elements/Window/WindowTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/Window/WindowTests.cs @@ -11,6 +11,7 @@ using Microsoft.Maui.Hosting; using Microsoft.Maui.Handlers; using Microsoft.Maui.Graphics; +using Microsoft.Maui.DeviceTests.Stubs; #if ANDROID || IOS using ShellHandler = Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer; diff --git a/src/Controls/tests/DeviceTests/HandlerTestBase.Android.cs b/src/Controls/tests/DeviceTests/HandlerTestBase.Android.cs index 85b2a4b67be1..6348d85e8ace 100644 --- a/src/Controls/tests/DeviceTests/HandlerTestBase.Android.cs +++ b/src/Controls/tests/DeviceTests/HandlerTestBase.Android.cs @@ -37,7 +37,7 @@ public AView GetSemanticPlatformElement(IViewHandler viewHandler) } static Drawable _decorDrawable; - Task RunWindowTest(IWindow window, Func action) + Task SetupWindowForTests(IWindow window, Func runTests) where THandler : class, IElementHandler { return InvokeOnMainThreadAsync(async () => @@ -60,18 +60,7 @@ Task RunWindowTest(IWindow window, Func action) .Commit(); await viewFragment.FinishedLoading; - - if (window.Content is Shell shell) - await OnLoadedAsync(shell.CurrentPage); - - if (typeof(THandler).IsAssignableFrom(window.Handler.GetType())) - await action((THandler)window.Handler); - else if (typeof(THandler).IsAssignableFrom(window.Content.Handler.GetType())) - await action((THandler)window.Content.Handler); - else if (window.Content is ContentPage cp && typeof(THandler).IsAssignableFrom(cp.Content.Handler.GetType())) - await action((THandler)cp.Content.Handler); - else - throw new Exception($"I can't work with {typeof(THandler)}"); + await runTests.Invoke(); } finally { diff --git a/src/Controls/tests/DeviceTests/HandlerTestBase.Windows.cs b/src/Controls/tests/DeviceTests/HandlerTestBase.Windows.cs index 955222b69338..66c067308d5e 100644 --- a/src/Controls/tests/DeviceTests/HandlerTestBase.Windows.cs +++ b/src/Controls/tests/DeviceTests/HandlerTestBase.Windows.cs @@ -1,23 +1,19 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Automation.Peers; using NativeAutomationProperties = Microsoft.UI.Xaml.Automation.AutomationProperties; -using WPanel = Microsoft.UI.Xaml.Controls.Panel; using WNavigationViewItem = Microsoft.UI.Xaml.Controls.NavigationViewItem; using WFrameworkElement = Microsoft.UI.Xaml.FrameworkElement; using WWindow = Microsoft.UI.Xaml.Window; -using Microsoft.Maui.Hosting; -using Microsoft.Maui.Handlers; using Microsoft.Maui; using Microsoft.Maui.Controls; -using Microsoft.Maui.Controls.Handlers; using Microsoft.Maui.Platform; using System.Threading.Tasks; using System; using Microsoft.Extensions.DependencyInjection; using System.Collections.Generic; -using WPoint = Windows.Foundation.Point; using WAppBarButton = Microsoft.UI.Xaml.Controls.AppBarButton; using Xunit; +using Microsoft.Maui.DeviceTests.Stubs; namespace Microsoft.Maui.DeviceTests { @@ -27,65 +23,29 @@ public partial class HandlerTestBase ((AccessibilityView)((DependencyObject)viewHandler.PlatformView).GetValue(NativeAutomationProperties.AccessibilityViewProperty)) == AccessibilityView.Content; - Task RunWindowTest(IWindow window, Func action) + + Task SetupWindowForTests(IWindow window, Func runTests) where THandler : class, IElementHandler { return InvokeOnMainThreadAsync(async () => { - var testingRootPanel = (WPanel)MauiProgram.CurrentWindow.Content; - IElementHandler newWindowHandler = null; - NavigationRootManager navigationRootManager = null; + var applicationContext = MauiContext.MakeApplicationScope(UI.Xaml.Application.Current); + + var appStub = new MauiAppNewWindowStub(window); + UI.Xaml.Application.Current.SetApplicationHandler(appStub, applicationContext); + WWindow newWindow = null; try { - var scopedContext = new MauiContext(MauiContext.Services); - scopedContext.AddWeakSpecific(MauiProgram.CurrentWindow); - var mauiContext = scopedContext.MakeScoped(true); - navigationRootManager = mauiContext.GetNavigationRootManager(); - - MauiContext - .Services - .GetRequiredService() - .SetWindowHandler(window, mauiContext); - - newWindowHandler = window.Handler; - var content = window.Content.Handler.ToPlatform(); - await content.OnLoadedAsync(); - await Task.Delay(10); - - if (typeof(THandler).IsAssignableFrom(newWindowHandler.GetType())) - await action((THandler)newWindowHandler); - else if (typeof(THandler).IsAssignableFrom(window.Content.Handler.GetType())) - await action((THandler)window.Content.Handler); - else if (window.Content is ContentPage cp && typeof(THandler).IsAssignableFrom(cp.Content.Handler.GetType())) - await action((THandler)cp.Content.Handler); - else - throw new Exception($"I can't work with {typeof(THandler)}"); - + ApplicationExtensions.CreatePlatformWindow(UI.Xaml.Application.Current, appStub, new Handlers.OpenWindowRequest()); + newWindow = window.Handler.PlatformView as WWindow; + await runTests.Invoke(); } finally { - if (navigationRootManager != null) - navigationRootManager.Disconnect(); - - if (newWindowHandler != null) - newWindowHandler.DisconnectHandler(); - - // Set the root window panel back to the testing panel - if (testingRootPanel != null && MauiProgram.CurrentWindow.Content != testingRootPanel) - { - MauiProgram.CurrentWindow.Content = testingRootPanel; - await testingRootPanel.OnLoadedAsync(); - await Task.Delay(10); - } - - // reset the appbar title to our test runner - MauiProgram - .CurrentWindow - .GetWindow() - .Handler - .MauiContext - .GetNavigationRootManager() - .UpdateAppTitleBar(true); + window.Handler.DisconnectHandler(); + await Task.Delay(10); + newWindow?.Close(); + appStub.Handler.DisconnectHandler(); } }); } @@ -176,7 +136,7 @@ protected MauiToolbar GetPlatformToolbar(IElementHandler handler) return true; } - + protected object GetTitleView(IElementHandler handler) { var toolbar = GetPlatformToolbar(handler); diff --git a/src/Controls/tests/DeviceTests/HandlerTestBase.cs b/src/Controls/tests/DeviceTests/HandlerTestBase.cs index 47ee5d0f86c2..d79897b10478 100644 --- a/src/Controls/tests/DeviceTests/HandlerTestBase.cs +++ b/src/Controls/tests/DeviceTests/HandlerTestBase.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Controls; @@ -68,6 +69,7 @@ public void EnsureHandlerCreated(Action additionalCreationAction handlers.AddHandler(typeof(VerticalStackLayout), typeof(LayoutHandler)); handlers.AddHandler(typeof(Controls.Window), typeof(WindowHandlerStub)); handlers.AddHandler(typeof(Controls.ContentPage), typeof(PageHandler)); + handlers.AddHandler(typeof(MauiAppNewWindowStub), typeof(ApplicationHandler)); }); appBuilder.Services.AddSingleton(svc => TestDispatcher.Provider); @@ -82,7 +84,7 @@ public void EnsureHandlerCreated(Action additionalCreationAction public void Dispose() { - ((IDisposable)_mauiApp).Dispose(); + ((IDisposable)_mauiApp)?.Dispose(); _mauiApp = null; _mauiContext = null; @@ -141,8 +143,8 @@ protected THandler CreateHandler(IElement element, IMauiContext mauiCo #else // Windows cannot measure without the view being loaded // iOS needs more love when I get an IDE again - var w = view.Width; - var h = view.Height; + var w = view.Width.Equals(double.NaN) ? -1 : view.Width; + var h = view.Height.Equals(double.NaN) ? -1 : view.Height; #endif view.Arrange(new Rect(0, 0, w, h)); @@ -174,6 +176,7 @@ protected Task CreateHandlerAndAddToWindow(IElement view, Action(IElement view, Func action) where THandler : class, IElementHandler { @@ -194,7 +197,37 @@ protected Task CreateHandlerAndAddToWindow(IElement view, Func(window, (handler) => action(handler as THandler)); + try + { + await _takeOverMainContentSempahore.WaitAsync(); + + await SetupWindowForTests(window, async () => + { + IView content = window.Content; + + if (content is IPageContainer pc) + content = pc.CurrentPage; + + await OnLoadedAsync(content as VisualElement); +#if WINDOWS + + await Task.Delay(10); +#endif + + if (typeof(THandler).IsAssignableFrom(window.Handler.GetType())) + await action((THandler)window.Handler); + else if (typeof(THandler).IsAssignableFrom(window.Content.Handler.GetType())) + await action((THandler)window.Content.Handler); + else if (window.Content is ContentPage cp && typeof(THandler).IsAssignableFrom(cp.Content.Handler.GetType())) + await action((THandler)cp.Content.Handler); + else + throw new Exception($"I can't work with {typeof(THandler)}"); + }); + } + finally + { + _takeOverMainContentSempahore.Release(); + } }); } diff --git a/src/Controls/tests/DeviceTests/HandlerTestBase.iOS.cs b/src/Controls/tests/DeviceTests/HandlerTestBase.iOS.cs index 40ff106806f2..2f8b8ac07682 100644 --- a/src/Controls/tests/DeviceTests/HandlerTestBase.iOS.cs +++ b/src/Controls/tests/DeviceTests/HandlerTestBase.iOS.cs @@ -4,6 +4,7 @@ using Microsoft.Maui.Controls.Handlers.Compatibility; using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.Controls.Platform.Compatibility; +using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Platform; using UIKit; @@ -23,7 +24,7 @@ protected bool GetExcludedWithChildren(IViewHandler viewHandler) return platformView.AccessibilityElementsHidden; } - Task RunWindowTest(IWindow window, Func action) + Task SetupWindowForTests(IWindow window, Func runTests) where THandler : class, IElementHandler { return InvokeOnMainThreadAsync(async () => @@ -31,21 +32,7 @@ Task RunWindowTest(IWindow window, Func action) try { _ = window.ToHandler(MauiContext); - IView content = window.Content; - - if (content is IPageContainer pc) - content = pc.CurrentPage; - - await OnLoadedAsync(content as VisualElement); - - if (typeof(THandler).IsAssignableFrom(window.Handler.GetType())) - await action((THandler)window.Handler); - else if (typeof(THandler).IsAssignableFrom(window.Content.Handler.GetType())) - await action((THandler)window.Content.Handler); - else if (window.Content is ContentPage cp && typeof(THandler).IsAssignableFrom(cp.Content.Handler.GetType())) - await action((THandler)cp.Content.Handler); - else - throw new Exception($"I can't work with {typeof(THandler)}"); + await runTests.Invoke(); } finally { @@ -91,7 +78,7 @@ protected bool IsBackButtonVisible(IElementHandler handler) protected object GetTitleView(IElementHandler handler) { var activeVC = GetVisibleViewController(handler); - if ( activeVC.NavigationItem.TitleView is + if (activeVC.NavigationItem.TitleView is ShellPageRendererTracker.TitleViewContainer tvc) { return tvc.View.Handler.PlatformView; diff --git a/src/Controls/tests/DeviceTests/Stubs/MauiAppNewWindowStub.cs b/src/Controls/tests/DeviceTests/Stubs/MauiAppNewWindowStub.cs new file mode 100644 index 000000000000..0503cecd89e3 --- /dev/null +++ b/src/Controls/tests/DeviceTests/Stubs/MauiAppNewWindowStub.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.Maui.DeviceTests.Stubs +{ + class MauiAppNewWindowStub : IApplication + { + readonly IWindow _window; + + public MauiAppNewWindowStub(IWindow window) + { + _window = window; + } + + public IReadOnlyList Windows => new List() { _window }; + + public IElementHandler Handler { get; set; } + + public IElement Parent => null; + + public void CloseWindow(IWindow window) + { + Handler?.Invoke(nameof(IApplication.CloseWindow), window); + } + + public IWindow CreateWindow(IActivationState activationState) + { + return _window; + } + + public void OpenWindow(IWindow window) + { + throw new NotImplementedException(); + } + + public void ThemeChanged() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Android.cs b/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Android.cs index fe5bc2872a8c..42f522d03b67 100644 --- a/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Android.cs +++ b/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Android.cs @@ -5,7 +5,7 @@ using static Microsoft.Maui.DeviceTests.HandlerTestBase; using AActivity = Android.App.Activity; -namespace Microsoft.Maui.DeviceTests +namespace Microsoft.Maui.DeviceTests.Stubs { public class WindowHandlerStub : ElementHandler, IWindowHandler { diff --git a/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Windows.cs b/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Windows.cs index 683eaf3859f5..f5f359e172c6 100644 --- a/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Windows.cs +++ b/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Windows.cs @@ -3,49 +3,15 @@ using WPanel = Microsoft.UI.Xaml.Controls.Panel; using WWindow = Microsoft.UI.Xaml.Window; using Microsoft.Maui.Handlers; +using Microsoft.UI.Xaml; -namespace Microsoft.Maui.DeviceTests +namespace Microsoft.Maui.DeviceTests.Stubs { - public class WindowHandlerStub : ElementHandler, IWindowHandler + public class WindowHandlerStub : WindowHandler { - public static IPropertyMapper WindowMapper = new PropertyMapper(WindowHandler.Mapper) - { - [nameof(IWindow.Content)] = MapContent - }; - - private static void MapContent(WindowHandlerStub handler, IWindow window) - { - _ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); - var windowManager = handler.MauiContext.GetNavigationRootManager(); - windowManager.Disconnect(); - windowManager.Connect(handler.VirtualView.Content.ToPlatform(handler.MauiContext)); - var rootPanel = handler.PlatformView.Content as WPanel; - - if (rootPanel == null) - return; - - if (!rootPanel.Children.Contains(windowManager.RootView)) - rootPanel.Children.Add(windowManager.RootView); - } - - protected override void DisconnectHandler(UI.Xaml.Window platformView) - { - var windowManager = MauiContext.GetNavigationRootManager(); - var rootPanel = platformView.Content as WPanel; - rootPanel.Children.Remove(windowManager.RootView); - windowManager.Disconnect(); - - base.DisconnectHandler(platformView); - } - public WindowHandlerStub() - : base(WindowMapper) - { - } - - protected override WWindow CreatePlatformElement() + : base() { - return MauiProgram.CurrentWindow; } } } diff --git a/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.iOS.cs b/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.iOS.cs index f0d363214b87..07ed96eb2ca0 100644 --- a/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.iOS.cs +++ b/src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.iOS.cs @@ -6,7 +6,7 @@ using Microsoft.Maui.Platform; using UIKit; -namespace Microsoft.Maui.DeviceTests +namespace Microsoft.Maui.DeviceTests.Stubs { public class WindowHandlerStub : ElementHandler, IWindowHandler { diff --git a/src/Core/src/Handlers/Window/WindowHandler.Windows.cs b/src/Core/src/Handlers/Window/WindowHandler.Windows.cs index 70aef9cb4159..0ae8c1234c50 100644 --- a/src/Core/src/Handlers/Window/WindowHandler.Windows.cs +++ b/src/Core/src/Handlers/Window/WindowHandler.Windows.cs @@ -11,7 +11,8 @@ protected override void DisconnectHandler(UI.Xaml.Window platformView) ?.GetNavigationRootManager() ?.Disconnect(); - platformView.Content = null; + if (platformView.Dispatcher != null) + platformView.Content = null; base.DisconnectHandler(platformView); } diff --git a/src/Core/src/Platform/Windows/StackNavigationManager.cs b/src/Core/src/Platform/Windows/StackNavigationManager.cs index 72381a252e20..e32060ab1d6c 100644 --- a/src/Core/src/Platform/Windows/StackNavigationManager.cs +++ b/src/Core/src/Platform/Windows/StackNavigationManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -15,6 +16,8 @@ public class StackNavigationManager IView? _currentPage; IMauiContext _mauiContext; Frame? _navigationFrame; + Action? _pendingNavigationFinished; + protected NavigationRootManager WindowManager => _mauiContext.GetNavigationRootManager(); internal IStackNavigation? NavigationView { get; private set; } public IReadOnlyList NavigationStack { get; set; } = new List(); @@ -34,6 +37,8 @@ public virtual void Connect(IStackNavigation navigationView, Frame navigationFra if (_navigationFrame != null) _navigationFrame.Navigated -= OnNavigated; + FirePendingNavigationFinished(); + navigationFrame.Navigated += OnNavigated; _navigationFrame = navigationFrame; NavigationView = (IStackNavigation)navigationView; @@ -47,6 +52,7 @@ public virtual void Disconnect(IStackNavigation navigationView, Frame navigation if (_navigationFrame != null) _navigationFrame.Navigated -= OnNavigated; + FirePendingNavigationFinished(); _navigationFrame = null; NavigationView = null; } @@ -146,7 +152,7 @@ void OnNavigated(object sender, UI.Xaml.Navigation.NavigationEventArgs e) if (e.Content is not Page page) return; - + var nv = NavigationView; ContentPresenter? presenter; if (page.Content == null) @@ -179,7 +185,7 @@ void OnNavigated(object sender, UI.Xaml.Navigation.NavigationEventArgs e) throw; } - fe.OnLoaded(() => + _pendingNavigationFinished = () => { if (presenter?.Content is not FrameworkElement pc) { @@ -194,12 +200,20 @@ void OnNavigated(object sender, UI.Xaml.Navigation.NavigationEventArgs e) { view.Arrange(fe); } - }); + }; + + fe.OnLoaded(FirePendingNavigationFinished); } void FireNavigationFinished() { + _pendingNavigationFinished = null; NavigationView?.NavigationFinished(NavigationStack); } + + void FirePendingNavigationFinished() + { + Interlocked.Exchange(ref _pendingNavigationFinished, null)?.Invoke(); + } } }