diff --git a/src/Controls/src/Core/NavigationPage/NavigationPage.cs b/src/Controls/src/Core/NavigationPage/NavigationPage.cs index 8bf135510018..64c2b264d3a9 100644 --- a/src/Controls/src/Core/NavigationPage/NavigationPage.cs +++ b/src/Controls/src/Core/NavigationPage/NavigationPage.cs @@ -783,8 +783,8 @@ protected async override Task OnPopAsync(bool animated) await Owner.SendHandlerUpdateAsync(animated, () => { - Owner.RemoveFromInnerChildren(currentPage); Owner.CurrentPage = newCurrentPage; + Owner.RemoveFromInnerChildren(currentPage); if (currentPage.TitleView != null) { currentPage.RemoveLogicalChild(currentPage.TitleView); diff --git a/src/Controls/src/Core/Page/Page.cs b/src/Controls/src/Core/Page/Page.cs index a119fe23e725..518ede72005f 100644 --- a/src/Controls/src/Core/Page/Page.cs +++ b/src/Controls/src/Core/Page/Page.cs @@ -305,12 +305,10 @@ public Task DisplayActionSheetAsync(string title, string cancel, string var args = new ActionSheetArguments(title, cancel, destruction, buttons); args.FlowDirection = flowDirection; -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter if (IsPlatformEnabled) - MessagingCenter.Send(this, ActionSheetSignalName, args); + Window.AlertManager.RequestActionSheet(this, args); else - _pendingActions.Add(() => MessagingCenter.Send(this, ActionSheetSignalName, args)); -#pragma warning restore CS0618 // Type or member is obsolete + _pendingActions.Add(() => Window.AlertManager.RequestActionSheet(this, args)); return args.Result.Task; } @@ -368,12 +366,10 @@ public Task DisplayAlertAsync(string title, string message, string accept, var args = new AlertArguments(title, message, accept, cancel); args.FlowDirection = flowDirection; -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter if (IsPlatformEnabled) - MessagingCenter.Send(this, AlertSignalName, args); + Window.AlertManager.RequestAlert(this, args); else - _pendingActions.Add(() => MessagingCenter.Send(this, AlertSignalName, args)); -#pragma warning restore CS0618 // Type or member is obsolete + _pendingActions.Add(() => Window.AlertManager.RequestAlert(this, args)); return args.Result.Task; } @@ -394,12 +390,10 @@ public Task DisplayAlertAsync(string title, string message, string accept, { var args = new PromptArguments(title, message, accept, cancel, placeholder, maxLength, keyboard, initialValue); -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter if (IsPlatformEnabled) - MessagingCenter.Send(this, PromptSignalName, args); + Window.AlertManager.RequestPrompt(this, args); else - _pendingActions.Add(() => MessagingCenter.Send(this, PromptSignalName, args)); -#pragma warning restore CS0618 // Type or member is obsolete + _pendingActions.Add(() => Window.AlertManager.RequestPrompt(this, args)); return args.Result.Task; } @@ -664,13 +658,11 @@ public void SendAppearing() if (IsBusy) { -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter if (IsPlatformEnabled) - MessagingCenter.Send(this, BusySetSignalName, true); + Window.AlertManager.RequestPageBusy(this, true); else - _pendingActions.Add(() => MessagingCenter.Send(this, BusySetSignalName, true)); + _pendingActions.Add(() => Window.AlertManager.RequestPageBusy(this, true)); } -#pragma warning restore CS0618 // Type or member is obsolete OnAppearing(); Appearing?.Invoke(this, EventArgs.Empty); @@ -693,10 +685,8 @@ public void SendDisappearing() _hasAppeared = false; -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter if (IsBusy) - MessagingCenter.Send(this, BusySetSignalName, false); -#pragma warning restore CS0618 // Type or member is obsolete + Window.AlertManager.RequestPageBusy(this, false); var pageContainer = this as IPageContainer; pageContainer?.CurrentPage?.SendDisappearing(); @@ -757,9 +747,7 @@ void OnPageBusyChanged() { if (!_hasAppeared) return; -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Send(this, BusySetSignalName, IsBusy); -#pragma warning restore CS0618 // Type or member is obsolete + Window.AlertManager.RequestPageBusy(this, IsBusy); } void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.Android.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Android.cs index 3a843b1fafd7..0d6a4961c941 100644 --- a/src/Controls/src/Core/Platform/AlertManager/AlertManager.Android.cs +++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Android.cs @@ -1,6 +1,7 @@ #nullable disable using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Android.App; using Android.Content; @@ -21,45 +22,15 @@ namespace Microsoft.Maui.Controls.Platform { internal partial class AlertManager { - readonly List Subscriptions = new List(); - - internal void Subscribe(Window window) + private partial IAlertManagerSubscription CreateSubscription(IMauiContext mauiContext) { - IMauiContext mauiContext = window?.MauiContext; - Context context = mauiContext?.Context; + Context context = mauiContext.Context; Activity activity = context.GetActivity(); - if (Subscriptions.Any(s => s.Activity == activity)) - { - return; - } - - Subscriptions.Add(new AlertRequestHelper(activity, mauiContext)); - } - - internal void Unsubscribe(Window window) - { - IMauiContext mauiContext = window?.Handler?.MauiContext; - Context context = mauiContext?.Context; - Activity activity = context?.GetActivity(); - if (activity == null) - return; - - var toRemove = Subscriptions.Where(s => s.Activity == activity).ToList(); - - foreach (AlertRequestHelper alertRequestHelper in toRemove) - { - alertRequestHelper.Dispose(); - Subscriptions.Remove(alertRequestHelper); - } - } - - internal void ResetBusyCount(Activity context) - { - Subscriptions.FirstOrDefault(s => s.Activity == context)?.ResetBusyCount(); + return new AlertRequestHelper(activity, mauiContext); } - internal sealed class AlertRequestHelper : IDisposable + internal sealed partial class AlertRequestHelper { int _busyCount; @@ -67,34 +38,13 @@ internal AlertRequestHelper(Activity context, IMauiContext mauiContext) { Activity = context; MauiContext = mauiContext; - -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Subscribe(Activity, Page.BusySetSignalName, OnPageBusy); - MessagingCenter.Subscribe(Activity, Page.AlertSignalName, OnAlertRequested); - MessagingCenter.Subscribe(Activity, Page.PromptSignalName, OnPromptRequested); - MessagingCenter.Subscribe(Activity, Page.ActionSheetSignalName, OnActionSheetRequested); -#pragma warning restore CS0618 // Type or member is obsolete } public Activity Activity { get; } - public IMauiContext MauiContext { get; } - public void Dispose() - { -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Unsubscribe(Activity, Page.BusySetSignalName); - MessagingCenter.Unsubscribe(Activity, Page.AlertSignalName); - MessagingCenter.Unsubscribe(Activity, Page.PromptSignalName); - MessagingCenter.Unsubscribe(Activity, Page.ActionSheetSignalName); -#pragma warning restore CS0618 // Type or member is obsolete - } - - public void ResetBusyCount() - { - _busyCount = 0; - } + public IMauiContext MauiContext { get; } - void OnPageBusy(IView sender, bool enabled) + public partial void OnPageBusy(Page sender, bool enabled) { // Verify that the page making the request is part of this activity if (!PageIsInThisContext(sender) && enabled) @@ -107,7 +57,7 @@ void OnPageBusy(IView sender, bool enabled) UpdateProgressBarVisibility(_busyCount > 0); } - void OnActionSheetRequested(IView sender, ActionSheetArguments arguments) + public partial void OnActionSheetRequested(Page sender, ActionSheetArguments arguments) { // Verify that the page making the request is part of this activity if (!PageIsInThisContext(sender)) @@ -166,7 +116,7 @@ void OnActionSheetRequested(IView sender, ActionSheetArguments arguments) } } - void OnAlertRequested(IView sender, AlertArguments arguments) + public partial void OnAlertRequested(Page sender, AlertArguments arguments) { // Verify that the page making the request is part of this activity if (!PageIsInThisContext(sender)) @@ -246,7 +196,7 @@ TextDirection GetTextDirection(IView sender, FlowDirection flowDirection) return TextDirection.Ltr; } - void OnPromptRequested(IView sender, PromptArguments arguments) + public partial void OnPromptRequested(Page sender, PromptArguments arguments) { // Verify that the page making the request is part of this activity if (!PageIsInThisContext(sender)) diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.Standard.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Standard.cs new file mode 100644 index 000000000000..6eeffec49b7b --- /dev/null +++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Standard.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.Maui.Controls.Internals; + +namespace Microsoft.Maui.Controls.Platform +{ + internal partial class AlertManager + { + private partial IAlertManagerSubscription CreateSubscription(IMauiContext mauiContext) + { + throw new NotImplementedException(); + } + + internal partial class AlertRequestHelper + { + public partial void OnActionSheetRequested(Page sender, ActionSheetArguments arguments) { } + + public partial void OnAlertRequested(Page sender, AlertArguments arguments) { } + + public partial void OnPromptRequested(Page sender, PromptArguments arguments) { } + + public partial void OnPageBusy(Page sender, bool enabled) { } + } + } +} \ No newline at end of file diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.Tizen.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Tizen.cs index 8db138b16164..b51d1bd1e97d 100644 --- a/src/Controls/src/Core/Platform/AlertManager/AlertManager.Tizen.cs +++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Tizen.cs @@ -1,6 +1,7 @@ #nullable disable using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Microsoft.Maui.Controls.Internals; @@ -12,36 +13,14 @@ namespace Microsoft.Maui.Controls.Platform { internal partial class AlertManager { - readonly List Subscriptions = new List(); - - internal void Subscribe(Window window) + private partial IAlertManagerSubscription CreateSubscription(IMauiContext mauiContext) { - var nativeWindow = window?.MauiContext.GetPlatformWindow(); - var modalStack = window?.MauiContext.GetModalStack(); - if (Subscriptions.Any(s => s.Window == nativeWindow)) - return; - - Subscriptions.Add(new AlertRequestHelper(nativeWindow, modalStack)); + var nativeWindow = mauiContext.GetPlatformWindow(); + var modalStack = mauiContext.GetModalStack(); + return new AlertRequestHelper(nativeWindow, modalStack); } - internal void Unsubscribe(Window window) - { - IMauiContext mauiContext = window?.Handler?.MauiContext; - var platformWindow = mauiContext?.GetPlatformWindow(); - if (platformWindow == null) - return; - - var toRemove = Subscriptions.Where(s => s.Window == platformWindow).ToList(); - - foreach (AlertRequestHelper alertRequestHelper in toRemove) - { - alertRequestHelper.Dispose(); - Subscriptions.Remove(alertRequestHelper); - } - } - } - - internal sealed class AlertRequestHelper : IDisposable + internal sealed partial class AlertRequestHelper { int _busyCount; Popup _busyPopup; @@ -51,29 +30,12 @@ internal sealed class AlertRequestHelper : IDisposable internal AlertRequestHelper(NWindow window, NavigationStack modalStack) { Window = window; - -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Subscribe(Window, Page.BusySetSignalName, OnBusySetRequest); - MessagingCenter.Subscribe(Window, Page.AlertSignalName, OnAlertRequest); - MessagingCenter.Subscribe(Window, Page.ActionSheetSignalName, OnActionSheetRequest); - MessagingCenter.Subscribe(Window, Page.PromptSignalName, OnPromptRequested); -#pragma warning restore CS0618 // Type or member is obsolete _modalStack = modalStack; } public NWindow Window { get; } - public void Dispose() - { -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Unsubscribe(Window, Page.AlertSignalName); - MessagingCenter.Unsubscribe(Window, Page.BusySetSignalName); - MessagingCenter.Unsubscribe(Window, Page.ActionSheetSignalName); - MessagingCenter.Unsubscribe(Window, Page.PromptSignalName); -#pragma warning restore CS0618 // Type or member is obsolete - } - - void OnBusySetRequest(Page sender, bool enabled) + public partial void OnPageBusy(Page sender, bool enabled) { // Verify that the page making the request is child of this platform if (!PageIsInThisWindow(sender)) @@ -99,7 +61,7 @@ void OnBusySetRequest(Page sender, bool enabled) } } - async void OnAlertRequest(Page sender, AlertArguments arguments) + public async partial void OnAlertRequested(Page sender, AlertArguments arguments) { // Verify that the page making the request is child of this platform if (!PageIsInThisWindow(sender)) @@ -130,7 +92,7 @@ await _modalStack.PushDummyPopupPage(async () => alert?.Dispose(); } - async void OnActionSheetRequest(Page sender, ActionSheetArguments arguments) + public async partial void OnActionSheetRequested(Page sender, ActionSheetArguments arguments) { // Verify that the page making the request is child of this platform if (!PageIsInThisWindow(sender)) @@ -150,12 +112,13 @@ await _modalStack.PushDummyPopupPage(async () => }); } - async void OnPromptRequested(Page sender, PromptArguments args) + public async partial void OnPromptRequested(Page sender, PromptArguments arguments) { // Verify that the page making the request is child of this platform if (!PageIsInThisWindow(sender)) return; + var args = arguments; await _modalStack.PushDummyPopupPage(async () => { @@ -203,4 +166,5 @@ protected override bool OnBackButtonPressed() } } } + } } diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.Windows.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Windows.cs index 7fd41c517ffd..d48b2eb2cdba 100644 --- a/src/Controls/src/Core/Platform/AlertManager/AlertManager.Windows.cs +++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.Windows.cs @@ -10,35 +10,14 @@ namespace Microsoft.Maui.Controls.Platform { internal partial class AlertManager { - readonly List Subscriptions = new List(); - - internal void Subscribe(Window window) + private partial IAlertManagerSubscription CreateSubscription(IMauiContext mauiContext) { - var platformWindow = window.MauiContext.GetPlatformWindow(); - - if (Subscriptions.Any(s => s.PlatformView == platformWindow)) - return; + var platformWindow = mauiContext.GetPlatformWindow(); - Subscriptions.Add(new AlertRequestHelper(window, platformWindow)); + return new AlertRequestHelper(Window, platformWindow); } - internal void Unsubscribe(Window window) - { - IMauiContext? mauiContext = window?.Handler?.MauiContext; - var platformWindow = mauiContext?.GetPlatformWindow(); - - var toRemove = platformWindow is null ? - Subscriptions.Where(s => s.VirtualView == window).ToList() : - Subscriptions.Where(s => s.PlatformView == platformWindow).ToList(); - - foreach (AlertRequestHelper alertRequestHelper in toRemove) - { - alertRequestHelper.Dispose(); - Subscriptions.Remove(alertRequestHelper); - } - } - - internal sealed class AlertRequestHelper : IDisposable + internal sealed partial class AlertRequestHelper { Task? CurrentAlert; Task? CurrentPrompt; @@ -47,35 +26,18 @@ internal AlertRequestHelper(Window virtualView, UI.Xaml.Window platformView) { VirtualView = virtualView; PlatformView = platformView; - -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Subscribe(PlatformView, Page.BusySetSignalName, OnPageBusy); - MessagingCenter.Subscribe(PlatformView, Page.AlertSignalName, OnAlertRequested); - MessagingCenter.Subscribe(PlatformView, Page.PromptSignalName, OnPromptRequested); - MessagingCenter.Subscribe(PlatformView, Page.ActionSheetSignalName, OnActionSheetRequested); -#pragma warning restore CS0618 // Type or member is obsolete } public Window VirtualView { get; } public UI.Xaml.Window PlatformView { get; } - public void Dispose() - { -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Unsubscribe(PlatformView, Page.BusySetSignalName); - MessagingCenter.Unsubscribe(PlatformView, Page.AlertSignalName); - MessagingCenter.Unsubscribe(PlatformView, Page.PromptSignalName); - MessagingCenter.Unsubscribe(PlatformView, Page.ActionSheetSignalName); -#pragma warning restore CS0618 // Type or member is obsolete - } - - void OnPageBusy(Page sender, bool enabled) + public partial void OnPageBusy(Page sender, bool enabled) { // TODO: Wrap the pages in a Canvas, and dynamically add a ProgressBar } - async void OnAlertRequested(Page sender, AlertArguments arguments) + public async partial void OnAlertRequested(Page sender, AlertArguments arguments) { if (!PageIsInThisWindow(sender)) return; @@ -131,7 +93,7 @@ async void OnAlertRequested(Page sender, AlertArguments arguments) CurrentAlert = null; } - async void OnPromptRequested(Page sender, PromptArguments arguments) + public async partial void OnPromptRequested(Page sender, PromptArguments arguments) { if (!PageIsInThisWindow(sender)) return; @@ -168,7 +130,7 @@ async void OnPromptRequested(Page sender, PromptArguments arguments) CurrentPrompt = null; } - void OnActionSheetRequested(Page sender, ActionSheetArguments arguments) + public partial void OnActionSheetRequested(Page sender, ActionSheetArguments arguments) { if (!PageIsInThisWindow(sender)) return; diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.cs index 05a22920aa86..b6d9c87db0ab 100644 --- a/src/Controls/src/Core/Platform/AlertManager/AlertManager.cs +++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.cs @@ -1,18 +1,88 @@ -#nullable disable +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Maui.Controls.Internals; + namespace Microsoft.Maui.Controls.Platform { internal partial class AlertManager { readonly Window _window; + IAlertManagerSubscription? _subscription; + public AlertManager(Window window) { _window = window; } - public void Subscribe() => Subscribe(_window); + public Window Window => _window; + + public IAlertManagerSubscription? Subscription => _subscription; + + public void Subscribe() + { + var context = _window.Handler?.MauiContext; + + if (context is null) + { + return; + } + + if (_subscription is not null) + { + context.CreateLogger()?.LogWarning("Warning - Window already had an alert manager subscription, but a new one was requested. Not going to do anything."); + return; + } + + _subscription = + // try use services + context.Services.GetService() ?? + // fall back to the platform implementation and a "null implementation" on non-platforms + CreateSubscription(context); + + if (_subscription is null) + { + context.CreateLogger()?.LogWarning("Warning - Unable to create alert manager subscription."); + } + } + + public void Unsubscribe() => + _subscription = null; + + public void RequestActionSheet(Page page, ActionSheetArguments arguments) => + _subscription?.OnActionSheetRequested(page, arguments); + + public void RequestAlert(Page page, AlertArguments arguments) => + _subscription?.OnAlertRequested(page, arguments); + + public void RequestPrompt(Page page, PromptArguments arguments) => + _subscription?.OnPromptRequested(page, arguments); + + public void RequestPageBusy(Page page, bool isBusy) => + _subscription?.OnPageBusy(page, isBusy); + + private partial IAlertManagerSubscription CreateSubscription(IMauiContext mauiContext); + + internal interface IAlertManagerSubscription + { + void OnActionSheetRequested(Page sender, ActionSheetArguments arguments); + + void OnAlertRequested(Page sender, AlertArguments arguments); + + void OnPromptRequested(Page sender, PromptArguments arguments); + + void OnPageBusy(Page sender, bool enabled); + } + + internal partial class AlertRequestHelper : IAlertManagerSubscription + { + public partial void OnActionSheetRequested(Page sender, ActionSheetArguments arguments); + + public partial void OnAlertRequested(Page sender, AlertArguments arguments); - public void Unsubscribe() => Unsubscribe(_window); + public partial void OnPromptRequested(Page sender, PromptArguments arguments); + public partial void OnPageBusy(Page sender, bool enabled); + } } } diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs index 2fb7db1f05c6..08b0e89ba4e0 100644 --- a/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs +++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs @@ -13,35 +13,13 @@ namespace Microsoft.Maui.Controls.Platform { internal partial class AlertManager { - readonly List Subscriptions = new List(); - - internal void Subscribe(Window window) + private partial IAlertManagerSubscription CreateSubscription(IMauiContext mauiContext) { - var platformWindow = window?.MauiContext.GetPlatformWindow(); - - if (Subscriptions.Any(s => s.PlatformView == platformWindow)) - return; - - Subscriptions.Add(new AlertRequestHelper(window, platformWindow)); - } - - internal void Unsubscribe(Window window) - { - IMauiContext mauiContext = window?.Handler?.MauiContext; - var platformWindow = mauiContext?.GetPlatformWindow(); - if (platformWindow == null) - return; - - var toRemove = Subscriptions.Where(s => s.PlatformView == platformWindow).ToList(); - - foreach (AlertRequestHelper alertRequestHelper in toRemove) - { - alertRequestHelper.Dispose(); - Subscriptions.Remove(alertRequestHelper); - } + var platformWindow = mauiContext.GetPlatformWindow(); + return new AlertRequestHelper(Window, platformWindow); } - internal sealed class AlertRequestHelper : IDisposable + internal sealed partial class AlertRequestHelper { const float AlertPadding = 10.0f; @@ -51,30 +29,13 @@ internal AlertRequestHelper(Window virtualView, UIWindow platformView) { VirtualView = virtualView; PlatformView = platformView; - -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Subscribe(PlatformView, Page.BusySetSignalName, OnPageBusy); - MessagingCenter.Subscribe(PlatformView, Page.AlertSignalName, OnAlertRequested); - MessagingCenter.Subscribe(PlatformView, Page.PromptSignalName, OnPromptRequested); - MessagingCenter.Subscribe(PlatformView, Page.ActionSheetSignalName, OnActionSheetRequested); -#pragma warning restore CS0618 // Type or member is obsolete } public Window VirtualView { get; } public UIWindow PlatformView { get; } - public void Dispose() - { -#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter - MessagingCenter.Unsubscribe(PlatformView, Page.BusySetSignalName); - MessagingCenter.Unsubscribe(PlatformView, Page.AlertSignalName); - MessagingCenter.Unsubscribe(PlatformView, Page.PromptSignalName); - MessagingCenter.Unsubscribe(PlatformView, Page.ActionSheetSignalName); -#pragma warning restore CS0618 // Type or member is obsolete - } - - void OnPageBusy(Page sender, bool enabled) + public partial void OnPageBusy(Page sender, bool enabled) { _busyCount = Math.Max(0, enabled ? _busyCount + 1 : _busyCount - 1); #pragma warning disable CA1416, CA1422 // TODO: 'UIApplication.NetworkActivityIndicatorVisible' is unsupported on: 'ios' 13.0 and later @@ -82,7 +43,7 @@ void OnPageBusy(Page sender, bool enabled) #pragma warning restore CA1416, CA1422 } - void OnAlertRequested(Page sender, AlertArguments arguments) + public partial void OnAlertRequested(Page sender, AlertArguments arguments) { if (!PageIsInThisWindow(sender)) return; @@ -90,7 +51,7 @@ void OnAlertRequested(Page sender, AlertArguments arguments) PresentAlert(sender, arguments); } - void OnPromptRequested(Page sender, PromptArguments arguments) + public partial void OnPromptRequested(Page sender, PromptArguments arguments) { if (!PageIsInThisWindow(sender)) return; @@ -98,7 +59,7 @@ void OnPromptRequested(Page sender, PromptArguments arguments) PresentPrompt(sender, arguments); } - void OnActionSheetRequested(Page sender, ActionSheetArguments arguments) + public partial void OnActionSheetRequested(Page sender, ActionSheetArguments arguments) { if (!PageIsInThisWindow(sender)) return; diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertMananger.Standard.cs b/src/Controls/src/Core/Platform/AlertManager/AlertMananger.Standard.cs deleted file mode 100644 index 46f06cb718d8..000000000000 --- a/src/Controls/src/Core/Platform/AlertManager/AlertMananger.Standard.cs +++ /dev/null @@ -1,16 +0,0 @@ -#nullable disable -using System; - -namespace Microsoft.Maui.Controls.Platform -{ - internal partial class AlertManager - { - internal static void Subscribe(Window window) - { - } - - internal static void Unsubscribe(Window window) - { - } - } -} diff --git a/src/Controls/src/Core/Window/Window.cs b/src/Controls/src/Core/Window/Window.cs index d13d7cb4499a..d48dbe5086bf 100644 --- a/src/Controls/src/Core/Window/Window.cs +++ b/src/Controls/src/Core/Window/Window.cs @@ -627,16 +627,18 @@ void OnPageChanged(Page? oldPage, Page? newPage) newPage.NavigationProxy.Inner = NavigationProxy; _menuBarTracker.Target = newPage; - if (Parent != null) - { - SendWindowAppearing(); - } - newPage.HandlerChanged += OnPageHandlerChanged; newPage.HandlerChanging += OnPageHandlerChanging; if (newPage.Handler != null) + { OnPageHandlerChanged(newPage, EventArgs.Empty); + } + + if (Parent != null) + { + SendWindowAppearing(); + } } if (newPage is Shell newShell) diff --git a/src/Controls/tests/Core.UnitTests/AlertManagerTests.cs b/src/Controls/tests/Core.UnitTests/AlertManagerTests.cs new file mode 100644 index 000000000000..0d52f54bf8da --- /dev/null +++ b/src/Controls/tests/Core.UnitTests/AlertManagerTests.cs @@ -0,0 +1,208 @@ +using System; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui.Controls.Platform; +using NSubstitute; +using Xunit; + +namespace Microsoft.Maui.Controls.Core.UnitTests +{ + public class AlertManagerTests : BaseTestFixture + { + private static (Window, AlertManager.IAlertManagerSubscription) CreateStubbedWindow(Action builder = null) + { + var stub = Substitute.For(); + + var window = CreateWindow(services => + { + services.GetService(Arg.Is(x => x == typeof(AlertManager.IAlertManagerSubscription))).Returns(stub); + builder?.Invoke(services); + }); + + return (window, stub); + } + + private static Window CreateWindow(Action builder = null) + { + var services = Substitute.For(); + builder?.Invoke(services); + + var mauiContext = Substitute.For(); + mauiContext.Services.Returns(services); + + var windowHandler = Substitute.For(); + windowHandler.MauiContext.Returns(mauiContext); + + var window = new Window(); + window.Handler = windowHandler; + + var app = Substitute.For(); + window.Parent = app; + + return window; + } + + [Fact] + public void TestsAreSetUpCorrectly() + { + var window = CreateWindow(); + + Assert.NotNull(window); + Assert.NotNull(window.AlertManager); + Assert.Null(window.AlertManager.Subscription); + } + + [Fact] + public void SettingPageWithoutHandlerDoesNotSubscribe() + { + var (window, sub) = CreateStubbedWindow(); + + window.Page = new ContentPage(); + + Assert.Null(window.AlertManager.Subscription); + window.MauiContext.Services.DidNotReceive().GetService(Arg.Is(x => x == typeof(AlertManager.IAlertManagerSubscription))); + } + + [Fact] + public void SettingPageWithHandlerSubscribes() + { + var (window, sub) = CreateStubbedWindow(); + var page = new ContentPage { Handler = Substitute.For() }; + window.Page = page; + + Assert.NotNull(window.AlertManager.Subscription); + Assert.Equal(sub, window.AlertManager.Subscription); + window.MauiContext.Services.Received().GetService(Arg.Is(x => x == typeof(AlertManager.IAlertManagerSubscription))); + } + + [Fact] + public void BusyNotSentWhenNotVisible() + { + var (window, sub) = CreateStubbedWindow(); + + var page = new ContentPage { IsBusy = true }; + window.Page = page; + + Assert.Null(window.AlertManager.Subscription); + } + + [Fact] + public void BusySentWhenBusyPageAppears() + { + var (window, sub) = CreateStubbedWindow(); + + var page = new ContentPage { IsBusy = true, Handler = Substitute.For() }; + window.Page = page; + + ((IPageController)page).SendAppearing(); + page.SendNavigatedTo(new NavigatedToEventArgs(null)); + + sub.Received().OnPageBusy(Arg.Is(page), Arg.Is(true)); + } + + [Fact] + public void BusySentWhenBusyPageDisappears() + { + var (window, sub) = CreateStubbedWindow(); + var page = new ContentPage { IsBusy = true, Handler = Substitute.For() }; + window.Page = page; + + ((IPageController)page).SendAppearing(); + page.SendNavigatedTo(new NavigatedToEventArgs(null)); + + sub.ClearReceivedCalls(); + + ((IPageController)page).SendDisappearing(); + + sub.Received().OnPageBusy(Arg.Is(page), Arg.Is(false)); + } + + [Fact] + public void BusySentWhenBusyPageIsNoLongerBusy() + { + var (window, sub) = CreateStubbedWindow(); + var page = new ContentPage { IsBusy = true, Handler = Substitute.For() }; + window.Page = page; + + ((IPageController)page).SendAppearing(); + page.SendNavigatedTo(new NavigatedToEventArgs(null)); + + sub.ClearReceivedCalls(); + + page.IsBusy = false; + + sub.Received().OnPageBusy(Arg.Is(page), Arg.Is(false)); + } + + [Fact] + public void BusySentWhenVisiblePageSetToBusy() + { + var (window, sub) = CreateStubbedWindow(); + var page = new ContentPage { Handler = Substitute.For() }; + window.Page = page; + + ((IPageController)page).SendAppearing(); + page.SendNavigatedTo(new NavigatedToEventArgs(null)); + + sub.ClearReceivedCalls(); + + page.IsBusy = true; + + sub.Received().OnPageBusy(Arg.Is(page), Arg.Is(true)); + } + + [Fact] + public void DisplayAlert() + { + var (window, sub) = CreateStubbedWindow(); + var page = new ContentPage() { Handler = Substitute.For(), IsPlatformEnabled = true }; + window.Page = page; + + AlertArguments args = null; + sub.When(x => x.OnAlertRequested(Arg.Any(), Arg.Any())).Do(x => args = x.Arg()); + + var task = page.DisplayAlert("Title", "Message", "Accept", "Cancel"); + + Assert.Equal("Title", args.Title); + Assert.Equal("Message", args.Message); + Assert.Equal("Accept", args.Accept); + Assert.Equal("Cancel", args.Cancel); + + bool completed = false; + var continueTask = task.ContinueWith(t => completed = true); + + args.SetResult(true); + continueTask.Wait(); + sub.Received().OnAlertRequested(Arg.Is(page), Arg.Is(args)); + Assert.True(completed); + } + + [Fact] + public void DisplayActionSheet() + { + var (window, sub) = CreateStubbedWindow(); + var page = new ContentPage() { Handler = Substitute.For(), IsPlatformEnabled = true }; + window.Page = page; + + ActionSheetArguments args = null; + sub.When(sub => sub.OnActionSheetRequested(Arg.Any(), Arg.Any())).Do(x => args = x.Arg()); + + var task = page.DisplayActionSheet("Title", "Cancel", "Destruction", "Other 1", "Other 2"); + + Assert.Equal("Title", args.Title); + Assert.Equal("Destruction", args.Destruction); + Assert.Equal("Cancel", args.Cancel); + Assert.Equal("Other 1", args.Buttons.First()); + Assert.Equal("Other 2", args.Buttons.Skip(1).First()); + + bool completed = false; + var continueTask = task.ContinueWith(t => completed = true); + + args.SetResult("Cancel"); + continueTask.Wait(); + sub.Received().OnActionSheetRequested(Arg.Is(page), Arg.Is(args)); + Assert.True(completed); + } + } +} diff --git a/src/Controls/tests/Core.UnitTests/PageTests.cs b/src/Controls/tests/Core.UnitTests/PageTests.cs index 14c6dd01bddb..37a10e2afab3 100644 --- a/src/Controls/tests/Core.UnitTests/PageTests.cs +++ b/src/Controls/tests/Core.UnitTests/PageTests.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Graphics; using Xunit; @@ -11,16 +9,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests public class PageTests : BaseTestFixture { - protected override void Dispose(bool disposing) - { - if (disposing) - { - MessagingCenter.ClearSubscribers(); - } - - base.Dispose(disposing); - } - [Fact] public void TestConstructor() { @@ -284,134 +272,6 @@ public void TestThrowOnInvalidAlignment() Assert.True(thrown); } - [Fact] - public void BusyNotSentWhenNotVisible() - { - var sent = false; - MessagingCenter.Subscribe(this, Page.BusySetSignalName, (p, b) => sent = true); - - new ContentPage { IsBusy = true }; - - Assert.False(sent); - } - - [Fact] - public async Task BusySentWhenBusyPageAppears() - { - TaskCompletionSource tcs = new TaskCompletionSource(); - var sent = false; - MessagingCenter.Subscribe(this, Page.BusySetSignalName, (p, b) => - { - Assert.True(b); - sent = true; - tcs.SetResult(); - }); - - var page = new ContentPage { IsBusy = true, IsPlatformEnabled = true }; - - Assert.False(sent); - - _ = new TestWindow(page); - - await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); - - Assert.True(sent, "Busy message not sent when visible"); - } - - [Fact] - public async Task BusySentWhenBusyPageDisappears() - { - TaskCompletionSource tcs = new TaskCompletionSource(); - var page = new ContentPage { IsBusy = true }; - _ = new TestWindow(page); - ((IPageController)page).SendAppearing(); - - var sent = false; - MessagingCenter.Subscribe(this, Page.BusySetSignalName, (p, b) => - { - Assert.False(b); - sent = true; - tcs.SetResult(); - }); - - ((IPageController)page).SendDisappearing(); - - await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); - - Assert.True(sent, "Busy message not sent when visible"); - } - - [Fact] - public async Task BusySentWhenVisiblePageSetToBusy() - { - var sent = false; - TaskCompletionSource tcs = new TaskCompletionSource(); - MessagingCenter.Subscribe(this, Page.BusySetSignalName, (p, b) => - { - sent = true; - tcs.SetResult(); - }); - - var page = new ContentPage(); - _ = new TestWindow(page); - ((IPageController)page).SendAppearing(); - - Assert.False(sent, "Busy message sent appearing while not busy"); - - page.IsBusy = true; - - await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); - - Assert.True(sent, "Busy message not sent when visible"); - } - - [Fact] - public void DisplayAlertAsync() - { - var page = new ContentPage() { IsPlatformEnabled = true }; - - AlertArguments args = null; - MessagingCenter.Subscribe(this, Page.AlertSignalName, (Page sender, AlertArguments e) => args = e); - - var task = page.DisplayAlertAsync("Title", "Message", "Accept", "Cancel"); - - Assert.Equal("Title", args.Title); - Assert.Equal("Message", args.Message); - Assert.Equal("Accept", args.Accept); - Assert.Equal("Cancel", args.Cancel); - - bool completed = false; - var continueTask = task.ContinueWith(t => completed = true); - - args.SetResult(true); - continueTask.Wait(); - Assert.True(completed); - } - - [Fact] - public void DisplayActionSheetAsync() - { - var page = new ContentPage() { IsPlatformEnabled = true }; - - ActionSheetArguments args = null; - MessagingCenter.Subscribe(this, Page.ActionSheetSignalName, (Page sender, ActionSheetArguments e) => args = e); - - var task = page.DisplayActionSheetAsync("Title", "Cancel", "Destruction", "Other 1", "Other 2"); - - Assert.Equal("Title", args.Title); - Assert.Equal("Destruction", args.Destruction); - Assert.Equal("Cancel", args.Cancel); - Assert.Equal("Other 1", args.Buttons.First()); - Assert.Equal("Other 2", args.Buttons.Skip(1).First()); - - bool completed = false; - var continueTask = task.ContinueWith(t => completed = true); - - args.SetResult("Cancel"); - continueTask.Wait(); - Assert.True(completed); - } - class PageTestApp : Application { } [Fact]