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

[Proposal] Modal windows for desktop apps #842

Open
8 tasks
mikeparker104 opened this issue Dec 23, 2022 · 9 comments
Open
8 tasks

[Proposal] Modal windows for desktop apps #842

mikeparker104 opened this issue Dec 23, 2022 · 9 comments
Labels
blocked dependencies Pull requests that update a dependency file new proposal A fully fleshed out proposal describing a new feature in syntactic and semantic detail

Comments

@mikeparker104
Copy link
Contributor

mikeparker104 commented Dec 23, 2022

Feature name

Modal Windows for desktop apps

Link to discussion

#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
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 UINSWindow is 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 the UINSWindow derives 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

@mikeparker104 mikeparker104 added new proposal A fully fleshed out proposal describing a new feature in syntactic and semantic detail labels Dec 23, 2022
@VladislavAntonyuk VladislavAntonyuk added the needs discussion Discuss it on the next Monthly standup label Jan 19, 2023
@mouralabank
Copy link

Any updates?

@VladislavAntonyuk VladislavAntonyuk removed the needs discussion Discuss it on the next Monthly standup label Mar 1, 2023
@VladislavAntonyuk
Copy link
Collaborator

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

@danielancines
Copy link

I'm very thrilled about this, modal window will be great!!

@SF-Simon
Copy link

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.

@VladislavAntonyuk
Copy link
Collaborator

On Windows it can be achieved using ContentDialog. However it is not a new window. Most likely we need WinApi interop.

@mouralabank
Copy link

Any updates on this issue?

@jfversluis jfversluis added the needs discussion Discuss it on the next Monthly standup label Jun 22, 2023
@mikeparker104
Copy link
Contributor Author

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.

@VladislavAntonyuk
Copy link
Collaborator

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.

@VladislavAntonyuk VladislavAntonyuk added blocked dependencies Pull requests that update a dependency file and removed needs discussion Discuss it on the next Monthly standup labels Jul 25, 2023
@lfmouradasilva
Copy link

Any updates?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked dependencies Pull requests that update a dependency file new proposal A fully fleshed out proposal describing a new feature in syntactic and semantic detail
Projects
None yet
Development

No branches or pull requests

7 participants