Skip to content

Add IAlertManager interface to enable custom AlertManager implementations - #33267

Closed
PureWeen with Copilot wants to merge 3 commits into
mainfrom
copilot/create-ialertmanager-interface
Closed

Add IAlertManager interface to enable custom AlertManager implementations#33267
PureWeen with Copilot wants to merge 3 commits into
mainfrom
copilot/create-ialertmanager-interface

Conversation

Copilot AI commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

Introduces IAlertManager interface to allow users to replace AlertManager behavior through dependency injection. Interface is internal with plans to make public in .NET 11.

Changes

  • IAlertManager interface: Defines public contract for alert, action sheet, and prompt operations

    • Includes Subscribe(), Unsubscribe(), RequestAlert(), RequestActionSheet(), RequestPrompt(), RequestPageBusy()
    • Marked internal with TODO to make public in .NET 11
  • AlertManager implementation: Now implements IAlertManager without behavioral changes

  • Window.AlertManager property:

    • Changed from concrete AlertManager to IAlertManager interface type
    • Lazy-initializes by checking services first, falls back to default AlertManager
    • Enables service-based customization: services.AddSingleton<IAlertManager>(customImpl)
  • Test updates: Cast to concrete type when accessing internal Subscription property

  • MockServiceProvider fix: Returns null for missing services instead of throwing (proper IServiceProvider contract)

Usage Pattern

// Custom implementation
public class CustomAlertManager : IAlertManager
{
    public void RequestAlert(Page page, AlertArguments arguments)
    {
        // Custom alert behavior
    }
    // ... other methods
}

// Registration
builder.Services.AddSingleton<IAlertManager>(sp => new CustomAlertManager(window));

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Original prompt

This section details on the original issue you should resolve

<issue_title>Create IAlertManager so users can replace the behavior of our AlertManager</issue_title>
<issue_description>### Description

In order to enable users to swap in their own AlertManager we need to create an IAlertManager interface and then convert all cases where AlertManager is created to use the Services to retrieve the alertmanager.

For this first implementation make the IAlertManager internal and we will make it public in NET11 so just add a TODO to make it public in NET11</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@PureWeen

Copy link
Copy Markdown
Member

/rebase

Copilot AI and others added 3 commits January 29, 2026 21:15
…ions

- Create internal IAlertManager interface with TODO to make public in NET11
- Make AlertManager implement IAlertManager
- Update Window to retrieve IAlertManager from services with fallback to default
- Update tests to cast to concrete AlertManager when checking internal state
- Add test to verify custom IAlertManager can be provided via services

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
The MockServiceProvider was throwing KeyNotFoundException when a service
wasn't registered, but IServiceProvider.GetService should return null.
This broke tests that relied on service resolution fallback behavior.

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
@github-actions
github-actions Bot force-pushed the copilot/create-ialertmanager-interface branch from 68b02ab to c85cc8d Compare January 29, 2026 21:15
kubaflo pushed a commit that referenced this pull request Apr 4, 2026
…4228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes #34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: #33267 (draft, creates internal `IAlertManager` for
#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
PureWeen pushed a commit that referenced this pull request Apr 8, 2026
…4228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes #34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: #33267 (draft, creates internal `IAlertManager` for
#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
devanathan-vaithiyanathan pushed a commit to devanathan-vaithiyanathan/maui that referenced this pull request Apr 9, 2026
…tnet#34228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes dotnet#34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: dotnet#33267 (draft, creates internal `IAlertManager` for
dotnet#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
PureWeen pushed a commit that referenced this pull request Apr 14, 2026
…4228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes #34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: #33267 (draft, creates internal `IAlertManager` for
#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
devanathan-vaithiyanathan pushed a commit to Tamilarasan-Paranthaman/maui that referenced this pull request Apr 21, 2026
…tnet#34228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes dotnet#34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: dotnet#33267 (draft, creates internal `IAlertManager` for
dotnet#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
Ahamed-Ali pushed a commit that referenced this pull request Apr 22, 2026
…4228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes #34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: #33267 (draft, creates internal `IAlertManager` for
#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
PureWeen pushed a commit that referenced this pull request Apr 22, 2026
…4228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes #34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: #33267 (draft, creates internal `IAlertManager` for
#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
PureWeen pushed a commit that referenced this pull request Apr 28, 2026
…4228)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Fixes #34104

Custom platform backends (e.g., Linux/GTK) currently have no supported
way to implement `DisplayAlert()`, `DisplayActionSheet()`, or
`DisplayPromptAsync()`. The entire alert pipeline (`AlertManager`,
`IAlertManagerSubscription`) is internal, forcing custom backends to use
`DispatchProxy` + heavy reflection to intercept dialog requests.

This PR introduces two public interfaces to enable custom platform
backends to implement alert dialogs without reflection:

### New Public APIs

**`IAlertManager`** — Public interface for the full alert management
lifecycle. Custom backends can implement this to completely replace the
default `AlertManager`:

```csharp
public interface IAlertManager
{
    void Subscribe();
    void Unsubscribe();
    void RequestAlert(Page page, AlertArguments arguments);
    void RequestActionSheet(Page page, ActionSheetArguments arguments);
    void RequestPrompt(Page page, PromptArguments arguments);
}
```

**`IAlertManagerSubscription`** — Public interface for platform-specific
dialog implementations. The default `AlertManager` already resolves this
from DI, so custom backends can register their implementation directly:

```csharp
public interface IAlertManagerSubscription
{
    void OnAlertRequested(Page sender, AlertArguments arguments);
    void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
    void OnPromptRequested(Page sender, PromptArguments arguments);
}
```

### Usage

Two levels of customization are now available:

```csharp
// Simple: Just provide custom dialog implementations
builder.Services.AddSingleton<IAlertManagerSubscription, GtkAlertSubscription>();

// Advanced: Replace the entire alert management system
builder.Services.AddSingleton<IAlertManager, CustomAlertManager>();
```

### Changes

- **New:** `IAlertManager` public interface in
`Microsoft.Maui.Controls.Platform`
- **New:** `IAlertManagerSubscription` public interface (moved from
internal nested interface)
- **Modified:** `AlertManager` now implements `IAlertManager`
- **Modified:** `Window.AlertManager` property typed as `IAlertManager`,
resolves custom implementation from DI
- **Modified:** Tests updated + 2 new tests for DI resolution
- **Updated:** All `PublicAPI.Unshipped.txt` files

### Related

- Related PR: #33267 (draft, creates internal `IAlertManager` for
#33266)
- Real-world use case: [Maui.Gtk
GtkAlertManager](https://github.com/redth/Maui.Gtk/blob/main/src/Platform.Maui.Linux.Gtk4/Platform/GtkAlertManager.cs)
— 370 lines of reflection that this PR eliminates

---------
kubaflo added a commit that referenced this pull request Apr 30, 2026
…PI changes) (#35095)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Description of Change

Enables third-party platform backends (for example Maui.Gtk) to provide
their own `DisplayAlertAsync` / `DisplayActionSheetAsync` /
`DisplayPromptAsync` implementations without MAUI exposing any new
public API. This complements #33267: because that PR introduces a new
public interface, it must wait for .NET 11. This PR solves the same
problem using already-shipped public argument types, so it is shippable
in .NET 10.

`AlertManager.Subscribe()` now keeps the existing explicit internal
`IAlertManagerSubscription` path, then checks for keyed delegate
registrations before falling back to the platform default. The delegate
signatures use only existing public types:

```csharp
Func<Page, AlertArguments,       Task<bool>>
Func<Page, ActionSheetArguments, Task<string>>
Func<Page, PromptArguments,      Task<string>>
```

The registrations are keyed so MAUI does not accidentally consume
unrelated `Func<>` services:

| Dialog | Service key |
|---|---|
| Alert | `Microsoft.Maui.Controls.DisplayAlert` |
| Action sheet | `Microsoft.Maui.Controls.DisplayActionSheet` |
| Prompt | `Microsoft.Maui.Controls.DisplayPrompt` |

Consumer usage:

```csharp
builder.Services.AddKeyedSingleton<Func<Page, AlertArguments, Task<bool>>>(
    "Microsoft.Maui.Controls.DisplayAlert",
    async (page, args) =>
    {
        return await MyGtkDialog.ShowAsync(args.Title, args.Message, args.Accept, args.Cancel);
    });
```

If any keyed delegate is registered, MAUI wraps it in a new internal
`DelegateAlertSubscription`. Registered operations dispatch to their
delegate; unregistered operations fall through to the platform default.
The delegate returns the dialog result and MAUI completes the
corresponding `AlertArguments`, `ActionSheetArguments`, or
`PromptArguments` internally. If the delegate returns `null`, faults, or
cancels, the caller observes that through the original `Display*Async`
task instead of hanging silently.

Precedence order in `Subscribe()`:

1. Explicit `IAlertManagerSubscription` service (existing internal path,
unchanged)
2. Keyed result-returning delegate convention (new)
3. Platform default subscription (existing fallback)

### Notes for reviewers

- Zero public API surface: no `PublicAPI.*.txt` changes. The new
`DelegateAlertSubscription` class and key constants are internal;
consumers use literal string keys documented on the existing argument
types.
- Keyed registrations are intentional. Unkeyed `Func<>` services are
ignored to avoid accidental collisions.
- The delegate returns the dialog result instead of calling
`args.SetResult(...)`. This matches the built-in async platform pattern
where MAUI completes the argument after awaiting the native dialog
result.
- Per-operation fall-through is intentional. A backend can override just
alerts and keep the platform action sheet/prompt, for example.
- `OnPageBusy` is excluded from the convention (obsolete in .NET 10,
removed in .NET 11) and always routes to the fallback.
- When .NET 11 ships a proper public
`IAlertManager`/`IAlertDialogProvider`, this delegate convention can
stay as a lightweight alias or be deprecated. Either way, .NET 10
consumers are unblocked now.

### Tests

Unit coverage in `AlertManagerTests.cs` includes:

- Alert, action sheet, and prompt delegate dispatch
- Returned delegate results completing the original `Display*Async`
caller
- Unregistered operation fall-through
- Unkeyed delegate services being ignored
- Explicit `IAlertManagerSubscription` precedence
- Synchronous and asynchronous delegate faults
- Delegate cancellation forwarding
- Null task contract violation

Focused `AlertManagerTests` pass locally: 21/21.

### Issues Fixed

Fixes #34104

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
github-actions Bot pushed a commit that referenced this pull request May 6, 2026
…PI changes) (#35095)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Description of Change

Enables third-party platform backends (for example Maui.Gtk) to provide
their own `DisplayAlertAsync` / `DisplayActionSheetAsync` /
`DisplayPromptAsync` implementations without MAUI exposing any new
public API. This complements #33267: because that PR introduces a new
public interface, it must wait for .NET 11. This PR solves the same
problem using already-shipped public argument types, so it is shippable
in .NET 10.

`AlertManager.Subscribe()` now keeps the existing explicit internal
`IAlertManagerSubscription` path, then checks for keyed delegate
registrations before falling back to the platform default. The delegate
signatures use only existing public types:

```csharp
Func<Page, AlertArguments,       Task<bool>>
Func<Page, ActionSheetArguments, Task<string>>
Func<Page, PromptArguments,      Task<string>>
```

The registrations are keyed so MAUI does not accidentally consume
unrelated `Func<>` services:

| Dialog | Service key |
|---|---|
| Alert | `Microsoft.Maui.Controls.DisplayAlert` |
| Action sheet | `Microsoft.Maui.Controls.DisplayActionSheet` |
| Prompt | `Microsoft.Maui.Controls.DisplayPrompt` |

Consumer usage:

```csharp
builder.Services.AddKeyedSingleton<Func<Page, AlertArguments, Task<bool>>>(
    "Microsoft.Maui.Controls.DisplayAlert",
    async (page, args) =>
    {
        return await MyGtkDialog.ShowAsync(args.Title, args.Message, args.Accept, args.Cancel);
    });
```

If any keyed delegate is registered, MAUI wraps it in a new internal
`DelegateAlertSubscription`. Registered operations dispatch to their
delegate; unregistered operations fall through to the platform default.
The delegate returns the dialog result and MAUI completes the
corresponding `AlertArguments`, `ActionSheetArguments`, or
`PromptArguments` internally. If the delegate returns `null`, faults, or
cancels, the caller observes that through the original `Display*Async`
task instead of hanging silently.

Precedence order in `Subscribe()`:

1. Explicit `IAlertManagerSubscription` service (existing internal path,
unchanged)
2. Keyed result-returning delegate convention (new)
3. Platform default subscription (existing fallback)

### Notes for reviewers

- Zero public API surface: no `PublicAPI.*.txt` changes. The new
`DelegateAlertSubscription` class and key constants are internal;
consumers use literal string keys documented on the existing argument
types.
- Keyed registrations are intentional. Unkeyed `Func<>` services are
ignored to avoid accidental collisions.
- The delegate returns the dialog result instead of calling
`args.SetResult(...)`. This matches the built-in async platform pattern
where MAUI completes the argument after awaiting the native dialog
result.
- Per-operation fall-through is intentional. A backend can override just
alerts and keep the platform action sheet/prompt, for example.
- `OnPageBusy` is excluded from the convention (obsolete in .NET 10,
removed in .NET 11) and always routes to the fallback.
- When .NET 11 ships a proper public
`IAlertManager`/`IAlertDialogProvider`, this delegate convention can
stay as a lightweight alias or be deprecated. Either way, .NET 10
consumers are unblocked now.

### Tests

Unit coverage in `AlertManagerTests.cs` includes:

- Alert, action sheet, and prompt delegate dispatch
- Returned delegate results completing the original `Display*Async`
caller
- Unregistered operation fall-through
- Unkeyed delegate services being ignored
- Explicit `IAlertManagerSubscription` precedence
- Synchronous and asynchronous delegate faults
- Delegate cancellation forwarding
- Null task contract violation

Focused `AlertManagerTests` pass locally: 21/21.

### Issues Fixed

Fixes #34104

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
@kubaflo

kubaflo commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Done here #34228

@kubaflo kubaflo closed this May 6, 2026
PureWeen pushed a commit that referenced this pull request Jun 2, 2026
…PI changes) (#35095)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Description of Change

Enables third-party platform backends (for example Maui.Gtk) to provide
their own `DisplayAlertAsync` / `DisplayActionSheetAsync` /
`DisplayPromptAsync` implementations without MAUI exposing any new
public API. This complements #33267: because that PR introduces a new
public interface, it must wait for .NET 11. This PR solves the same
problem using already-shipped public argument types, so it is shippable
in .NET 10.

`AlertManager.Subscribe()` now keeps the existing explicit internal
`IAlertManagerSubscription` path, then checks for keyed delegate
registrations before falling back to the platform default. The delegate
signatures use only existing public types:

```csharp
Func<Page, AlertArguments,       Task<bool>>
Func<Page, ActionSheetArguments, Task<string>>
Func<Page, PromptArguments,      Task<string>>
```

The registrations are keyed so MAUI does not accidentally consume
unrelated `Func<>` services:

| Dialog | Service key |
|---|---|
| Alert | `Microsoft.Maui.Controls.DisplayAlert` |
| Action sheet | `Microsoft.Maui.Controls.DisplayActionSheet` |
| Prompt | `Microsoft.Maui.Controls.DisplayPrompt` |

Consumer usage:

```csharp
builder.Services.AddKeyedSingleton<Func<Page, AlertArguments, Task<bool>>>(
    "Microsoft.Maui.Controls.DisplayAlert",
    async (page, args) =>
    {
        return await MyGtkDialog.ShowAsync(args.Title, args.Message, args.Accept, args.Cancel);
    });
```

If any keyed delegate is registered, MAUI wraps it in a new internal
`DelegateAlertSubscription`. Registered operations dispatch to their
delegate; unregistered operations fall through to the platform default.
The delegate returns the dialog result and MAUI completes the
corresponding `AlertArguments`, `ActionSheetArguments`, or
`PromptArguments` internally. If the delegate returns `null`, faults, or
cancels, the caller observes that through the original `Display*Async`
task instead of hanging silently.

Precedence order in `Subscribe()`:

1. Explicit `IAlertManagerSubscription` service (existing internal path,
unchanged)
2. Keyed result-returning delegate convention (new)
3. Platform default subscription (existing fallback)

### Notes for reviewers

- Zero public API surface: no `PublicAPI.*.txt` changes. The new
`DelegateAlertSubscription` class and key constants are internal;
consumers use literal string keys documented on the existing argument
types.
- Keyed registrations are intentional. Unkeyed `Func<>` services are
ignored to avoid accidental collisions.
- The delegate returns the dialog result instead of calling
`args.SetResult(...)`. This matches the built-in async platform pattern
where MAUI completes the argument after awaiting the native dialog
result.
- Per-operation fall-through is intentional. A backend can override just
alerts and keep the platform action sheet/prompt, for example.
- `OnPageBusy` is excluded from the convention (obsolete in .NET 10,
removed in .NET 11) and always routes to the fallback.
- When .NET 11 ships a proper public
`IAlertManager`/`IAlertDialogProvider`, this delegate convention can
stay as a lightweight alias or be deprecated. Either way, .NET 10
consumers are unblocked now.

### Tests

Unit coverage in `AlertManagerTests.cs` includes:

- Alert, action sheet, and prompt delegate dispatch
- Returned delegate results completing the original `Display*Async`
caller
- Unregistered operation fall-through
- Unkeyed delegate services being ignored
- Explicit `IAlertManagerSubscription` precedence
- Synchronous and asynchronous delegate faults
- Delegate cancellation forwarding
- Null task contract violation

Focused `AlertManagerTests` pass locally: 21/21.

### Issues Fixed

Fixes #34104

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create IAlertManager so users can replace the behavior of our AlertManager

3 participants