Skip to content

Feature/maui/sl popup v2 #552

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

Merged
merged 24 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions docs/maui/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ items:
href: views/Popup.md
- name: "Popup Service"
href: views/popup-service.md
- name: "Customizing Popup behavior and appearance"
href: views/popup/popup-options.md
- name: "Returning a value from a Popup"
href: views/popup/popup-result.md
- name: SemanticOrderView
href: views/semantic-order-view.md
- name: C# Markup
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/maui/images/views/popup/popup-no-border.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/maui/images/views/popup/popup-no-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/maui/images/views/popup/popup-result.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
312 changes: 109 additions & 203 deletions docs/maui/views/Popup.md

Large diffs are not rendered by default.

73 changes: 52 additions & 21 deletions docs/maui/views/popup-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@ The following sections will incrementally build on how to use the `PopupService`

## Creating a Popup

In order to use the `PopupService` to present or close a `Popup` the `Popup` must first be registered. Based on the steps in [Defining your popup](./Popup.md#defining-your-popup) the following can be created.
In order to use the `PopupService` to present or close a `Popup` the `Popup` must first be registered. Based on the steps in [Displaying a Popup](./Popup.md#displaying-a-popup) the following can be created.

The XAML contents of the `Popup` can be defined as:

```xaml
<toolkit:Popup
<ContentView
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:viewModels="clr-namespace:MyProject.ViewModels"
x:Class="MyProject.Popups.NamePopup"
HorizontalOptions="Center"
VerticalOptions="Center"
Padding="10"
Spacing="6"
x:DataType="viewModels:NamePopupViewModel">

<VerticalStackLayout>
Expand All @@ -34,18 +37,17 @@ The XAML contents of the `Popup` can be defined as:
<Button Text="Cancel" Command="{Binding CancelCommand}" />
</VerticalStackLayout>

</toolkit:Popup>
</ContentView>
```

The C# contents of the `Popup` can be defined as:

```csharp
using CommunityToolkit.Maui.Views;
using MyProject.ViewModels;

namespace MyProject.Popups;

public partial class NamePopup : Popup
public partial class NamePopup : ContentView
{
public NamePopup(NamePopupViewModel namePopupViewModel)
{
Expand All @@ -61,7 +63,7 @@ The backing view model for the `Popup` can be defined as:
public class NamePopupViewModel : ObservableObject
{
[ObservableProperty]
string name = "";
public partial string Name { get; set; } = string.Empty;

readonly IPopupService popupService;

Expand Down Expand Up @@ -97,7 +99,7 @@ builder.Services.AddTransientPopup<NamePopup, NamePopupViewModel>();

The .NET MAUI Community Toolkit provides a mechanism to instantiate and present popups in a .NET MAUI application. The popup service is automatically registered with the `MauiAppBuilder` when using the `UseMauiCommunityToolkit` initialization method. This enables you to resolve an `IPopupService` implementation in any part of your application.

The `IPopupService` makes it possible to register a popup view and its associated view model. The ability to show a `Popup` can now be driven by only providing the view model making it possible to keep a clean separation between view and view model.
The `IPopupService` makes it possible to register a popup view and its associated view model. The ability to show a `Popup` requires a developer to pass in the current `INavigation` implementation for the current window in the application. The easiest way to achieve this is through the following example.

The following example shows how to use the `IPopupService` to create and display a popup in a .NET MAUI application:

Expand All @@ -113,17 +115,17 @@ public class MyViewModel : INotifyPropertyChanged

public void DisplayPopup()
{
this.popupService.ShowPopup<NamePopupViewModel>();
this.popupService.ShowPopup<NamePopupViewModel>(Shell.Current);
}
}
```

Alternatively the caller can await the ShowPopupAsync method in order to handle a [result being returned](#returning-a-result). The `DisplayPopup` method can be rewritten as:

```csharp
public void DisplayPopup()
public async Task DisplayPopup()
{
var name = await this.popupService.ShowPopupAsync<NamePopupViewModel>();
var result = await this.popupService.ShowPopupAsync<NamePopupViewModel>(currentPage.Navigation);
}
```

Expand All @@ -133,9 +135,24 @@ The `IPopupService` also provides methods to handle a result being returned from

## Passing data to a Popup view model

When presenting a Popup we sometimes need to pass data across to the underlying view model to allow for dynamic content to be presented to the user. The `IPopupService` makes this possible through the overloads of the `ShowPopup` and `ShowPopupAsync` methods that takes a `Action<TViewModel> onPresenting` parameter. This parameter has been designed to be framework agnostic and allow you as a developer to drive the loading/passing of data however best fits your architecture.
When presenting a Popup we sometimes need to pass data across to the underlying view model to allow for dynamic content to be presented to the user. `IPopupService` makes this possible through the overloads of the `ShowPopup` and `ShowPopupAsync` methods that takes a `IDictionary<string, object> shellParameters` parameter. This makes use of the `IQueryAttributable` interface provided with .NET MAUI Shell, for more information on using this please refer to [Process navigation data using a single method](/dotnet/maui/fundamentals/shell/navigation#process-navigation-data-using-a-single-method).

To extend the previous example of showing a `NamePopupViewModel` and its associated Popup, we can use the `onPresenting` parameter to pass in the users name:
To extend the previous example of showing a `NamePopupViewModel` and its associated Popup, we can extend the `NamePopupViewModel` to implement the `IQueryAttributable` interface as follows:

```csharp
public class NamePopupViewModel : ObservableObject, IQueryAttributable
{
[ObservableProperty]
public partial string Name { get; set; } = "";

public void ApplyQueryAttributes(IDictionary<string, object> query)
{
Name = (string)query[nameof(NamePopupViewModel.Name)];
}
}
```

Then the view model that presents the popup and passes the data can look as follows:

```csharp
public class MyViewModel : INotifyPropertyChanged
Expand All @@ -147,26 +164,34 @@ public class MyViewModel : INotifyPropertyChanged
this.popupService = popupService;
}

public void DisplayPopup()
public async Task DisplayPopup()
{
this.popupService.ShowPopup<UpdatingPopupViewModel>(onPresenting: viewModel => viewModel.Name = "Shaun");
var queryAttributes = new Dictionary<string, object>
{
[nameof(NamePopupViewModel.Name)] = "Shaun"
};

await this.popupService.ShowPopupAsync<NamePopupViewModel>(
Shell.Current,
options: PopupOptions.Empty,
shellParameters: queryAttributes);
}
}
```

## Closing a Popup

The `PopupService` provides the `ClosePopup` and `ClosePopupAsync` methods that make it possible to close a `Popup` from a view model.
The `PopupService` provides the `ClosePopupAsync` method that makes it possible to close a `Popup` from a view model.

### Programmatically closing a Popup

Expanding on the previous example the following implementation can be added to the `OnCancel` method:

```csharp
[RelayCommand]
void OnCancel()
async Task OnCancel()
{
popupService.ClosePopup();
await popupService.ClosePopupAsync(Shell.Current);
}
```

Expand All @@ -180,13 +205,13 @@ Expanding on the previous example the following implementation can be added to t

```csharp
[RelayCommand(CanExecute = nameof(CanSave))]
void OnSave()
async Task OnSave()
{
popupService.ClosePopup(Name);
await popupService.ClosePopupAsync(Shell.Current, Name);
}
```

This will result in the most recently displayed `Popup` being closed and the caller being return the value in `Name`.
This will result in the most recently displayed `Popup` being closed and the caller being return the value in `Name` wrapped inside of an `IPopupResult<T>` implementation. For more information on creating a `Popup` that can return a result see [`Popup` - Returning a result](./popup/popup-result.md).

## Examples

Expand All @@ -195,3 +220,9 @@ You can find an example of this feature in action in the [.NET MAUI Community To
## API

You can find the source code for `Popup` over on the [.NET MAUI Community Toolkit GitHub repository](https://github.com/CommunityToolkit/Maui/tree/main/src/CommunityToolkit.Maui/Views/Popup).

## Additional Resources

- [`Popup` - Returning a result](./popup/popup-result.md)
- [`IPopupService`](./popup-service.md)
- [`PopupOptions` - Customizing a `Popup` behavior and appearance](./popup/popup-options.md)
139 changes: 139 additions & 0 deletions docs/maui/views/popup/popup-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
title: PopupOptions - .NET MAUI Community Toolkit
author: bijington
description: The PopupOptions class provides the ability to customize the appearance and behavior of the Popup.
ms.date: 05/19/2025
---

# PopupOptions - Customizing the popup behavior and appearance

The `PopupOptions` class provides the ability to customize the appearance and behavior of the `Popup`.

All of the examples in this page make use of the `SimplePopup` example that is covered at [`Popup`](../popup.md).

## Controlling whether the user can dismiss the Popup

`PopupOptions` provides the `CanBeDismissedByTappingOutsideOfPopup` property which can control the whether the user can tap or click outside of the Popup bounds to dismiss the currently displayed Popup. This is `true` by default. The following example shows how to prevent a user from being able to dismiss the Popup.

```csharp
await this.ShowPopupAsync(new SimplePopup(), new PopupOptions
{
CanBeDismissedByTappingOutsideOfPopup = false
});
```

> [!NOTE]
> It is possible to cache `PopupOptions` to reuse on future navigation actions or with other popups that share the same configuration, that would prevent unnecessary allocations.

## Customize the Overlay Color

`PopupOptions` provides the `PageOverlayColor` property which can control the `Color` that overlays the current page. The following example shows how to set the `PageOverlayColor` to orange.

```csharp
await this.ShowPopupAsync(new SimplePopup(), new PopupOptions
{
PageOverlayColor = Colors.Orange
});
```

![Popup page overlay](../../images/views/popup/popup-page-overlay-opaque.png "Popup rendering with an opaque orange background")

The above `Popup` renders with an opaque background, to control the opacity modify the alpha property of a `Color`. The following example shows how to

```csharp
await this.ShowPopupAsync(new SimplePopup(), new PopupOptions
{
PageOverlayColor = Colors.Orange.WithAlpha(0.5f)
});
```

![Popup page transparent overlay](../../images/views/popup/popup-page-overlay-translucent.png "Popup rendering with a translucent orange background")

## Customize the Popup Border

`PopupOptions` provides the `Shape` property which can control the appearance of the border around the content displayed in the `Popup`. The following example shows how to set the border to be a rounded rectangle with a corner radius of 4 and a stroke of blue.

```csharp
await this.ShowPopupAsync(new SimplePopup(), new PopupOptions
{
Shape = new RoundRectangle
{
CornerRadius = new CornerRadius(4),
Stroke = Colors.Blue,
StrokeThickness = 4
}
});
```

![Popup blue border stroke](../../images/views/popup/popup-blue-border.png "Popup rendering with a blue border")

For more details on how to customize the `Shape` property see [.NET MAUI Shapes](/dotnet/maui/user-interface/controls/shapes/).

### Disable the Popup Border

In order to disable the border on a Popup, simply set the `Shape` property to `null`. The following example shows how to achieve this

```csharp
await this.ShowPopupAsync(new SimplePopup(), new PopupOptions
{
Shape = null
});
```

![Popup no border](../../images/views/popup/popup-no-border.png "Popup rendering with no border")

## Customize the Popup Shadow

`PopupOptions` provides the `Shadow` property which can control the shadow which is applied to the `Popup`. The following example shows how to set the shadow to be green with an opacity of 80%.

```csharp
await this.ShowPopupAsync(new SimplePopup(), new PopupOptions
{
Shadow = new Shadow
{
Brush = Brush.Green,
Opacity = 0.8f
}
});
```

![Popup green shadow](../../images/views/popup/popup-green-shadow.png "Popup rendering with a green shadow")

For more details on how to customize the `Shadow` property see [.NET MAUI Shadow](/dotnet/maui/user-interface/shadow).

### Disable the Popup Shadow

In order to disable the border on a Popup, simply set the `Shape` property to `null`. The following example shows how to achieve this

```csharp
await this.ShowPopupAsync(new SimplePopup(), new PopupOptions
{
Shadow = null
});
```

![Popup no shadow](../../images/views/popup/popup-no-shadow.png "Popup rendering with no shadow")

## Properties

|Property |Type |Description |
|---------|---------|---------|
| `CanBeDismissedByTappingOutsideOfPopup` | `bool` | Gets or sets a value indicating whether the popup can be dismissed by tapping outside of the Popup. On Android - when false the hardware back button is disabled. |
| `OnTappingOutsideOfPopup` | `Action` | Gets or sets the `Action` to be executed when the user taps outside the `Popup`. |
| `PageOverlayColor` | `Color` | Gets or sets the `Color` of the overlay behind the `Popup`. |
| `Shape` | `Shape` | Gets or sets the border shape for the `Popup`. Set this to `null` to remove any border styling. |
| `Shadow` | `Shadow` | Gets or sets the `Shadow` of the `Popup`. Set this to `null` to remove any shadow. |

## Examples

You can find an example of this feature in action in the [.NET MAUI Community Toolkit Sample Application](https://github.com/CommunityToolkit/Maui/blob/main/samples/CommunityToolkit.Maui.Sample/Pages/Views/Popups/ReturnResultPopup.xaml).

## API

You can find the source code for `PopupOptions` over on the [.NET MAUI Community Toolkit GitHub repository](https://github.com/CommunityToolkit/Maui/tree/main/src/CommunityToolkit.Maui/Views/Popup).

## Additional Resources

- [`Popup`](../popup.md)
- [`IPopupService`](../popup-service.md)
- [`Popup` - Returning a result](./popup-result.md)
Loading