Maui
Maui copied to clipboard
[Proposal] Modal windows for desktop apps
Feature name
Modal Windows for desktop apps
Link to discussion
https://github.com/CommunityToolkit/Maui/discussions/840
Progress tracker
- [ ] Android Implementation
- [ ] iOS Implementation
- [ ] MacCatalyst Implementation
- [ ] Windows Implementation
- [ ] Tizen Implementation
- [ ] Unit Tests
- [ ] Samples
- [ ] Documentation
Summary
Enable use of modal windows in multi-window desktop apps that:
- Can be moved beyond the confines of a specific window e.g., so it can be displayed next to the window or on another monitor
- Prevents interaction with all other application windows until that modal window has been closed
- Keeps focus (and appears on top of the other application windows) even when other application windows receive input
- Does not appear alongside other windows in the taskbar and/or switcher
Existing options, within .NET MAUI and other third-party components, only support the display of a Page and/or dialog that is constrained to the Window it was opened from.
Motivation
In desktop apps, a modal window is often used to ensure a task or flow is completed before further interaction with application functionality is able to take place on any other window in the application. For example, in Visual Studio when a user needs to sign in, switch accounts, or change some global settings. This feature would make it easier for .NET MAUI developers to use modal windows and follow paradigms commonly used by desktop apps.
Detailed Design
ModalWindow.cs
This is principally to allow for a platform-specific handler that can perform actions where needed before the CreatePlatformElement method returns the native Window / UIWindow.
public sealed class ModalWindow : Window
{
public ModalWindow() : base() {}
public ModalWindow(Page page) : base(page) {}
}
INavigationExtensions.cs
Extends INavigation with a version of INavigation.PushModalAsync that will show the specified Page in a modal Window if supported but otherwise use the current method to display it within the current Window.
namespace Microsoft.Maui.Controls;
public static class INavigationExtensions
{
public static Task PushModalAsyncEx(this INavigation navigation, Page page)
{
#if MACCATALYST || WINDOWS
Application.Current.OpenWindow(new ModalWindow(page));
return Task.CompletedTask;
#else
return navigation.PushModalAsync(page);
#endif
}
}
Custom WindowHandler implementations would need to orchestrate the modal configuration/behavior using the requisite platform APIs, such as those identified below (see Platform APIs). On MacCatalyst, a custom MauiUISceneDelegate could potentially get allocated to the UIWindow.WindowScene to handle the configuration and running/stopping of the modal loop when the respective UIWindow opens and closes. Likewise on Windows, the Xaml.Window could be configured and run as a modal on creation then stop the modal behavior on closing.
Platform APIs
The expectation is that this would require use of the following platform-specific APIs.
MacCatalyst
A modal event loop can be started and stopped for a specific NSWindow using the following APIs from the NSApplication class:
The standard buttons on a given NSWindow can be acquired by type using standardWindowButton: and configured as needed.
WinUI3
A combination of Win32 and Windows App SDK Interop APIs alongside the Windows App SDK components as described in an answer to a recent forum question and inferred from usage in WPF. Notable members, types, and APIs include:
Types and Members
- AppWindow.IsShownInSwitchers
- AppWindow.Presenter
- OverlappedPresenter.IsMaximizable
- OverlappedPresenter.IsMinimizable
- OverlappedPresenter.IsResizable
Win32 APIs and Constants
Usage Syntax
MainPage (code-behind)
The following is an indicative Button Clicked event handler from a ContentPage (MainPage) that opens a modal window hosting the specified ContentPage (MyPage).
public partial class MainPage : ContentPage
{
void OnButtonClicked(object sender, EventArgs e)
=> _ = Navigation.PushModalAsyncEx(new MyPage());
}
Drawbacks
The requisite platform APIs aren't exposed directly by the MacCatalyst and WinUI3 SDKs. A challenge common to both MacCatalyst and WinUI3 is the potential to miss key details of the implementation when orchestrating several separate lower-level APIs or not anticipating things that may impact the modal behavior. The effort and approaches required to build this feature will also vary per platform and have their own considerations.
MacCatalyst
This feature would require use of foundational AppKit APIs and Types that aren't exposed by the MacCatalyst SDK. Notably NSApplication, NSWindow, and manipulation of its standard window buttons.
MacCatalyst currently depends on several AppKit Types and concepts. For example, a MacCatalyst specific class called UINSWindow is created when a scene is activated and is added to the NSApplication.sharedApplication.windows array. This is a special private class that's essentially a bridge between UIWindow and NSWindow allowing scenes to be presented as Mac windows. UINSWindow inherits from NSWindow and so its technically possible to invoke its functionality at runtime and pass it to other AppKit classes, including for use with the aforementioned NSApplication functions.
While the NSApplication, NSWindow, and NSButton types cannot be used directly in MacCatalyst, it's possible to get a reference to those types and invoke the requisite functionality on them via selectors.
Key Considerations
- There's a risk that Apple could remove or prevent use of the requisite AppKit APIs by a MacCatalyst app in future
- The
UINSWindowis a private class. If you use it directly in a MacCatalyst app, and its use is detected, it might be considered grounds for rejection during the App Store review process. It's not clear whether getting a reference to and calling functionality on the NSWindow (which theUINSWindowderives from) would be detectable and/or considered in the same way since it's public even if it's not exposed by the MacCatalyst SDK.
WinUI3
There's an open proposal for adding modal dialog support to WinUI3 but it does not currently support this concept directly. Therefore, this feature would require orchestration of several lower level APIs making it more challenging and error prone when compared to calling a couple of high-level APIs.
Key Considerations
- Effort associated with ensuring the correct sequencing and handling of the requisite lower level APIs
- Change if modal dialog support is added to WinUI3 in future
Alternatives
No response
Unresolved Questions
No response
Any updates?
TODO:
- Can this be achieved using .NET MAUI Multi Windows API
- Can this be achieved using the extension method? ShowModal(content), where we create a window with modal attributes
I'm very thrilled about this, modal window will be great!!
Hello, @mikeparker104
Is there any code to share? I am very looking forward to this proposal and really want to achieve it quickly. Let's complete this idea together.
Thank you for your time.
On Windows it can be achieved using ContentDialog. However it is not a new window. Most likely we need WinApi interop.
Any updates on this issue?
Hello, @mikeparker104
Is there any code to share? I am very looking forward to this proposal and really want to achieve it quickly. Let's complete this idea together.
Thank you for your time.
Hello @SF-Simon. Thanks for supporting this suggestion. I've created a quick prototype (maui-modals) to explore the concept a little further and as input into the development of this proposal.
Hello @mikeparker104 Thank you for your modal window implementation. It works great. Unfortunately it relies on interop and it's not really reliable (may be broken with any .NET MAUI or windows/macOS update). For now we don't plan to include it in the library.
Any updates?