CommunityToolkit / Maui

The .NET MAUI Community Toolkit is a community-created library that contains .NET MAUI Extensions, Advanced UI/UX Controls, and Behaviors to help make your life as a .NET MAUI developer easier
https://learn.microsoft.com/dotnet/communitytoolkit/maui
MIT License
2.29k stars 403 forks source link

[Proposal] Modal windows for desktop apps #842

Open mikeparker104 opened 1 year ago

mikeparker104 commented 1 year ago

Feature name

Modal Windows for desktop apps

Link to discussion

https://github.com/CommunityToolkit/Maui/discussions/840

Progress tracker

Summary

Enable use of modal windows in multi-window desktop apps that:

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

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

Alternatives

No response

Unresolved Questions

No response

mouralabank commented 1 year ago

Any updates?

VladislavAntonyuk commented 1 year ago

TODO:

danielancines commented 1 year ago

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

SF-Simon commented 1 year ago

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 commented 1 year ago

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

mouralabank commented 1 year ago

Any updates on this issue?

mikeparker104 commented 1 year ago

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 commented 1 year ago

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.

lfmouradasilva commented 9 months ago

Any updates?