diff --git a/src/Controls/samples/Controls.Sample/Pages/Core/ModalPage.xaml b/src/Controls/samples/Controls.Sample/Pages/Core/ModalPage.xaml
new file mode 100644
index 000000000000..a3d7f04a69c0
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample/Pages/Core/ModalPage.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample/Pages/Core/ModalPage.xaml.cs b/src/Controls/samples/Controls.Sample/Pages/Core/ModalPage.xaml.cs
new file mode 100644
index 000000000000..bc1bfc67f6bf
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample/Pages/Core/ModalPage.xaml.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
+
+namespace Maui.Controls.Sample.Pages
+{
+ public partial class ModalPage
+ {
+ public ModalPage()
+ {
+ InitializeComponent();
+ BackgroundColor = Colors.Purple;
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ PushModal.IsVisible = Navigation.ModalStack.Count > 0;
+ }
+
+ async void PushModalClicked(object sender, EventArgs e)
+ {
+ await Navigation.PushModalAsync(new ModalPage()
+ {
+ BackgroundColor =
+ (BackgroundColor == Colors.Purple) ? Colors.Pink : Colors.Purple
+ });
+ }
+
+ async void PopModalClicked(object sender, EventArgs e)
+ {
+ await Navigation.PopModalAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample/ViewModels/CoreViewModel.cs b/src/Controls/samples/Controls.Sample/ViewModels/CoreViewModel.cs
index f278bf0f47ca..6395cc2d6ee2 100644
--- a/src/Controls/samples/Controls.Sample/ViewModels/CoreViewModel.cs
+++ b/src/Controls/samples/Controls.Sample/ViewModels/CoreViewModel.cs
@@ -26,6 +26,9 @@ protected override IEnumerable CreateItems() => new[]
new SectionModel(typeof(TransformationsPage), "Transformations",
"Apply scale transformations, rotation, etc. to a View."),
+
+ new SectionModel(typeof(ModalPage), "Modal",
+ "Allows you to push and pop Modal Pages."),
};
}
}
\ No newline at end of file
diff --git a/src/Controls/src/Core/HandlerImpl/Window.Impl.cs b/src/Controls/src/Core/HandlerImpl/Window.Impl.cs
index 6aa9a68f8e75..57f631697641 100644
--- a/src/Controls/src/Core/HandlerImpl/Window.Impl.cs
+++ b/src/Controls/src/Core/HandlerImpl/Window.Impl.cs
@@ -26,7 +26,7 @@ public partial class Window : NavigableElement, IWindow
public Window()
{
AlertManager = new AlertManager(this);
- ModalNavigationService = new ModalNavigationService(this);
+ ModalNavigationManager = new ModalNavigationManager(this);
Navigation = new NavigationImpl(this);
InternalChildren.CollectionChanged += OnCollectionChanged;
@@ -75,7 +75,7 @@ protected override void OnPropertyChanged([CallerMemberName] string? propertyNam
internal AlertManager AlertManager { get; }
- internal ModalNavigationService ModalNavigationService { get; }
+ internal ModalNavigationManager ModalNavigationManager { get; }
internal IMauiContext MauiContext =>
Handler?.MauiContext ?? throw new InvalidOperationException("MauiContext is null.");
@@ -138,7 +138,7 @@ static void OnPageChanged(BindableObject bindable, object oldValue, object newVa
newPage.NavigationProxy.Inner = window.NavigationProxy;
}
- window.ModalNavigationService.SettingNewPage();
+ window.ModalNavigationManager.SettingNewPage();
if (newPage != null)
{
@@ -148,7 +148,7 @@ static void OnPageChanged(BindableObject bindable, object oldValue, object newVa
void OnPageAttachedHandler(object? sender, EventArgs e)
{
- window.ModalNavigationService.PageAttachedHandler();
+ window.ModalNavigationManager.PageAttachedHandler();
window.AlertManager.Subscribe();
}
@@ -208,19 +208,19 @@ public NavigationImpl(Window owner)
protected override IReadOnlyList GetModalStack()
{
- return _owner.ModalNavigationService.ModalStack;
+ return _owner.ModalNavigationManager.ModalStack;
}
protected override async Task OnPopModal(bool animated)
{
- Page modal = _owner.ModalNavigationService.ModalStack[_owner.ModalNavigationService.ModalStack.Count - 1];
+ Page modal = _owner.ModalNavigationManager.ModalStack[_owner.ModalNavigationManager.ModalStack.Count - 1];
if (_owner.OnModalPopping(modal))
{
_owner.OnPopCanceled();
return null;
}
- Page result = await _owner.ModalNavigationService.PopModalAsync(animated);
+ Page result = await _owner.ModalNavigationManager.PopModalAsync(animated);
result.Parent = null;
_owner.OnModalPopped(result);
return result;
@@ -235,11 +235,11 @@ protected override async Task OnPushModal(Page modal, bool animated)
if (modal.NavigationProxy.ModalStack.Count == 0)
{
modal.NavigationProxy.Inner = this;
- await _owner.ModalNavigationService.PushModalAsync(modal, animated);
+ await _owner.ModalNavigationManager.PushModalAsync(modal, animated);
}
else
{
- await _owner.ModalNavigationService.PushModalAsync(modal, animated);
+ await _owner.ModalNavigationManager.PushModalAsync(modal, animated);
modal.NavigationProxy.Inner = this;
}
diff --git a/src/Controls/src/Core/HandlerImpl/Window.Windows.cs b/src/Controls/src/Core/HandlerImpl/Window.Windows.cs
new file mode 100644
index 000000000000..35388dba4234
--- /dev/null
+++ b/src/Controls/src/Core/HandlerImpl/Window.Windows.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Maui.Controls
+{
+ public partial class Window
+ {
+ internal UI.Xaml.Window NativeWindow =>
+ (Handler?.NativeView as UI.Xaml.Window) ?? throw new InvalidOperationException("Window Handler should have a Window set.");
+ }
+}
diff --git a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Android.cs b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs
similarity index 98%
rename from src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Android.cs
rename to src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs
index 4658af9edfd3..8a227199b9ac 100644
--- a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Android.cs
+++ b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs
@@ -12,7 +12,7 @@
namespace Microsoft.Maui.Controls.Platform
{
- internal partial class ModalNavigationService
+ internal partial class ModalNavigationManager
{
partial void OnPageAttachedHandler()
{
@@ -166,9 +166,9 @@ internal bool HandleBackPressed()
class BackButtonCallBack : OnBackPressedCallback
{
WeakReference _weakReference;
- ModalNavigationService? _service;
+ ModalNavigationManager? _service;
- public BackButtonCallBack(ModalNavigationService service, Context context) : base(true)
+ public BackButtonCallBack(ModalNavigationManager service, Context context) : base(true)
{
_service = service;
_weakReference = new WeakReference(context);
diff --git a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Standard.cs b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Standard.cs
similarity index 88%
rename from src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Standard.cs
rename to src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Standard.cs
index 23ead192620c..c8893d0583f8 100644
--- a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Standard.cs
+++ b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Standard.cs
@@ -7,7 +7,7 @@
namespace Microsoft.Maui.Controls.Platform
{
- internal partial class ModalNavigationService
+ internal partial class ModalNavigationManager
{
public Task PopModalAsync(bool animated)
{
diff --git a/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Windows.cs b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Windows.cs
new file mode 100644
index 000000000000..eba41a3b2a74
--- /dev/null
+++ b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Windows.cs
@@ -0,0 +1,154 @@
+#nullable enable
+
+using System;
+using System.Threading.Tasks;
+using Microsoft.Maui.Graphics;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Microsoft.Maui.Controls.Platform
+{
+ internal partial class ModalNavigationManager
+ {
+ Page? _modalBackgroundPage;
+
+ Panel Container
+ {
+ get
+ {
+ if (_window.NativeWindow.Content is Panel p)
+ return p;
+
+ throw new InvalidOperationException("Root container Panel not found");
+ }
+ }
+
+ Rectangle ContainerBounds
+ {
+ get { return new Rectangle(0, 0, Container.ActualWidth, Container.ActualHeight); }
+ }
+
+ public Task PopModalAsync(bool animated)
+ {
+ var tcs = new TaskCompletionSource();
+ var currentPage = _navModel.CurrentPage;
+ Page result = _navModel.PopModal();
+ SetCurrent(_navModel.CurrentPage, currentPage, true, () => tcs.SetResult(result));
+ return tcs.Task;
+ }
+
+ public Task PushModalAsync(Page modal, bool animated)
+ {
+ if (modal == null)
+ throw new ArgumentNullException(nameof(modal));
+
+ var tcs = new TaskCompletionSource();
+ var currentPage = _navModel.CurrentPage;
+ _navModel.PushModal(modal);
+ SetCurrent(modal, currentPage, false, () => tcs.SetResult(true));
+ return tcs.Task;
+ }
+
+ void AddPage(Page page)
+ {
+ if (Container == null || page == null)
+ return;
+
+ if (_modalBackgroundPage != null)
+ _modalBackgroundPage.GetCurrentPage()?.SendDisappearing();
+
+ page.ToNative(MauiContext);
+
+ var pageHandler = (INativeViewHandler)page.Handler;
+
+
+ if (pageHandler.ContainerView != null && !Container.Children.Contains(pageHandler.ContainerView))
+ Container.Children.Add(pageHandler.ContainerView);
+ else if (!Container.Children.Contains(pageHandler.NativeView))
+ Container.Children.Add(pageHandler.NativeView);
+
+ (page as IFrameworkElement).Measure(Container.ActualWidth, Container.ActualHeight);
+ (page as IFrameworkElement).Arrange(ContainerBounds);
+
+ page.Layout(ContainerBounds);
+ }
+
+ void RemovePage(Page page)
+ {
+ if (Container == null || page == null)
+ return;
+
+ if (_modalBackgroundPage != null)
+ _modalBackgroundPage.GetCurrentPage()?.SendAppearing();
+
+ var pageHandler = (INativeViewHandler)page.Handler;
+
+ if (Container.Children.Contains(pageHandler.NativeView))
+ Container.Children.Remove(pageHandler.NativeView);
+
+ if (Container.Children.Contains(pageHandler.ContainerView))
+ Container.Children.Remove(pageHandler.ContainerView);
+ }
+
+ partial void OnPageAttachedHandler()
+ {
+ if (_modalBackgroundPage != null)
+ {
+ RemovePage(_modalBackgroundPage);
+ _modalBackgroundPage.Cleanup();
+ _modalBackgroundPage.Parent = null;
+ }
+ }
+
+ void SetCurrent(Page newPage, Page previousPage, bool popping, Action? completedCallback = null)
+ {
+ bool modal = true;
+ try
+ {
+ if (modal && !popping && !newPage.BackgroundColor.IsDefault())
+ _modalBackgroundPage = previousPage;
+ else
+ {
+ RemovePage(previousPage);
+
+ if (!modal && _modalBackgroundPage != null)
+ {
+ RemovePage(_modalBackgroundPage);
+ _modalBackgroundPage.Cleanup();
+ _modalBackgroundPage.Parent = null;
+ }
+
+ _modalBackgroundPage = null;
+ }
+
+ if (popping)
+ {
+ //// Un-parent the page; otherwise the Resources Changed Listeners won't be unhooked and the
+ //// page will leak
+
+ previousPage.Cleanup();
+ previousPage.Parent = null;
+ }
+
+ newPage.Layout(ContainerBounds);
+
+ AddPage(newPage);
+
+ completedCallback?.Invoke();
+
+ // TODO MAUI WINUI STill needs a Toolbar
+ //UpdateToolbarTracker();
+ //await UpdateToolbarItems();
+ }
+ catch (Exception error)
+ {
+ //This exception prevents the Main Page from being changed in a child
+ //window or a different thread, except on the Main thread.
+ //HEX 0x8001010E
+ if (error.HResult == -2147417842)
+ throw new InvalidOperationException("Changing the current page is only allowed if it's being called from the same UI thread." +
+ "Please ensure that the new page is in the same UI thread as the current page.");
+ throw;
+ }
+ }
+ }
+}
diff --git a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.cs b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.cs
similarity index 93%
rename from src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.cs
rename to src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.cs
index ad410e17de77..da11ff92bf23 100644
--- a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.cs
+++ b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.cs
@@ -8,7 +8,7 @@
namespace Microsoft.Maui.Controls.Platform
{
- internal partial class ModalNavigationService
+ internal partial class ModalNavigationManager
{
Window _window;
public IReadOnlyList ModalStack => _navModel.Modals;
@@ -17,7 +17,7 @@ internal partial class ModalNavigationService
NavigationModel? _previousNavModel = null;
Page? _previousPage;
- public ModalNavigationService(Window window)
+ public ModalNavigationManager(Window window)
{
_window = window;
}
@@ -60,6 +60,7 @@ internal void SettingNewPage()
}
partial void OnPageAttachedHandler();
+
public void PageAttachedHandler() => OnPageAttachedHandler();
}
}
diff --git a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.iOS.cs b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.iOS.cs
similarity index 98%
rename from src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.iOS.cs
rename to src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.iOS.cs
index 0b9f0b9be312..ea561a457e80 100644
--- a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.iOS.cs
+++ b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.iOS.cs
@@ -10,7 +10,7 @@
namespace Microsoft.Maui.Controls.Platform
{
- internal partial class ModalNavigationService
+ internal partial class ModalNavigationManager
{
UIViewController? _renderer
{
diff --git a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Windows.cs b/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Windows.cs
deleted file mode 100644
index c984abca971a..000000000000
--- a/src/Controls/src/Core/Platform/ModalNavigationService/ModalNavigationService.Windows.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-#nullable enable
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Microsoft.Maui.Controls.Platform
-{
- internal partial class ModalNavigationService
- {
- public Task PopModalAsync(bool animated)
- {
- throw new NotImplementedException();
- }
-
- public Task PushModalAsync(Page modal, bool animated)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/src/Controls/src/Core/Platform/iOS/Extensions/PageExtensions.cs b/src/Controls/src/Core/Platform/PageExtensions.cs
similarity index 98%
rename from src/Controls/src/Core/Platform/iOS/Extensions/PageExtensions.cs
rename to src/Controls/src/Core/Platform/PageExtensions.cs
index 64eaf1e7931f..6af659f657db 100644
--- a/src/Controls/src/Core/Platform/iOS/Extensions/PageExtensions.cs
+++ b/src/Controls/src/Core/Platform/PageExtensions.cs
@@ -1,6 +1,5 @@
using System;
using System.Linq;
-using UIKit;
namespace Microsoft.Maui.Controls.Platform
{