Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WinUI Modal Navigation #1563

Merged
merged 2 commits into from
Jul 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Controls/samples/Controls.Sample/Pages/Core/ModalPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Pages.ModalPage"
xmlns:views="clr-namespace:Maui.Controls.Sample.Pages.Base">
<views:BasePage.Content>
<StackLayout
Margin="12">
<Button Clicked="PushModalClicked" Text="Push Modal Page" />
<Button x:Name="PushModal" Clicked="PopModalClicked" Text="Pop Modal Page" />
</StackLayout>
</views:BasePage.Content>
</views:BasePage>
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ protected override IEnumerable<SectionModel> 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."),
};
}
}
18 changes: 9 additions & 9 deletions src/Controls/src/Core/HandlerImpl/Window.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.");
Expand Down Expand Up @@ -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)
{
Expand All @@ -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();
}

Expand Down Expand Up @@ -208,19 +208,19 @@ public NavigationImpl(Window owner)

protected override IReadOnlyList<Page> GetModalStack()
{
return _owner.ModalNavigationService.ModalStack;
return _owner.ModalNavigationManager.ModalStack;
}

protected override async Task<Page?> 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;
Expand All @@ -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;
}

Expand Down
12 changes: 12 additions & 0 deletions src/Controls/src/Core/HandlerImpl/Window.Windows.cs
Original file line number Diff line number Diff line change
@@ -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.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace Microsoft.Maui.Controls.Platform
{
internal partial class ModalNavigationService
internal partial class ModalNavigationManager
{
partial void OnPageAttachedHandler()
{
Expand Down Expand Up @@ -166,9 +166,9 @@ internal bool HandleBackPressed()
class BackButtonCallBack : OnBackPressedCallback
{
WeakReference<Context> _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>(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Microsoft.Maui.Controls.Platform
{
internal partial class ModalNavigationService
internal partial class ModalNavigationManager
{
public Task<Page> PopModalAsync(bool animated)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Page> PopModalAsync(bool animated)
{
var tcs = new TaskCompletionSource<Page>();
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<bool>();
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;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Microsoft.Maui.Controls.Platform
{
internal partial class ModalNavigationService
internal partial class ModalNavigationManager
{
Window _window;
public IReadOnlyList<Page> ModalStack => _navModel.Modals;
Expand All @@ -17,7 +17,7 @@ internal partial class ModalNavigationService
NavigationModel? _previousNavModel = null;
Page? _previousPage;

public ModalNavigationService(Window window)
public ModalNavigationManager(Window window)
{
_window = window;
}
Expand Down Expand Up @@ -60,6 +60,7 @@ internal void SettingNewPage()
}

partial void OnPageAttachedHandler();

public void PageAttachedHandler() => OnPageAttachedHandler();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Microsoft.Maui.Controls.Platform
{
internal partial class ModalNavigationService
internal partial class ModalNavigationManager
{
UIViewController? _renderer
{
Expand Down

This file was deleted.

Loading