This package provides ReactiveUI bindings and helpers for the Mopups library in .NET MAUI. It enables you to build composable, reactive popup views using the Model-View-ViewModel (MVVM) pattern.
Install the package into your MAUI project:
To initialize the plugin, you must register it within your MauiProgram.cs file. This ensures that the Mopups services are configured and the ReactiveUI interactions are wired up correctly.
using Microsoft.Maui.Hosting;
using ReactiveUI.Maui.Plugins.Popup;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
// Initialize ReactiveUI Popups
.ConfigureReactiveUIPopup();
return builder.Build();
}
}Inherit from ReactiveObject to gain access to reactive properties and commands.
using System.Reactive;
using ReactiveUI;
public class ConfirmViewModel : ReactiveObject
{
public ReactiveCommand<Unit, Unit> ConfirmCommand { get; }
public ReactiveCommand<Unit, Unit> CancelCommand { get; }
public ConfirmViewModel()
{
ConfirmCommand = ReactiveCommand.Create(() => { /* handle confirm */ });
CancelCommand = ReactiveCommand.Create(() => { /* handle cancel */ });
}
}Inherit from ReactivePopupPage<TViewModel> to create a strongly-typed popup.
using ReactiveUI;
using ReactiveUI.Maui.Plugins.Popup;
public partial class ConfirmPopup : ReactivePopupPage<ConfirmViewModel>
{
public ConfirmPopup()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.BindCommand(ViewModel, vm => vm.ConfirmCommand, v => v.ConfirmButton)
.DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CancelCommand, v => v.CancelButton)
.DisposeWith(disposables);
});
}
}var popup = new ConfirmPopup { ViewModel = new ConfirmViewModel() };
this.Navigation.PushPopup(popup).Subscribe();This library provides both Hot and Cold observables for different use cases:
The navigation methods (PopAllPopup, PopPopup, PushPopup, RemovePopupPage) return Cold Observables created from async operations.
Key Characteristics:
- The operation does not begin until the observable is subscribed to
- Each subscription triggers a new execution of the async operation
- The observable completes after emitting a single value
- Ideal for command-driven navigation operations
Example:
// The popup is NOT pushed until Subscribe() is called
var pushOperation = this.Navigation.PushPopup(myPopup);
// Subscribing triggers the push operation
pushOperation.Subscribe();
// Each subscription triggers a new push (be careful!)
pushOperation.Subscribe(); // This would push the popup again!The event observables (PoppingObservable, PoppedObservable, PushingObservable, PushedObservable) return Hot Observables derived from events.
Key Characteristics:
- Produce values regardless of whether there are active subscriptions
- Events fire independently of subscription state
- Multiple subscribers receive the same event notifications
- Ideal for monitoring and reacting to navigation events
Example:
// Monitor all popup pushes in your application
var popupService = MopupService.Instance;
popupService.PushedObservable()
.Subscribe(args =>
{
Debug.WriteLine($"Popup pushed: {args}");
});
// Multiple subscribers can observe the same events
popupService.PoppedObservable()
.Subscribe(args =>
{
Debug.WriteLine($"Popup dismissed: {args}");
});All navigation methods are available as extension methods on both INavigation and IPopupNavigation.
Removes all popup pages from the popup navigation stack in a single operation.
// Using INavigation
this.Navigation.PopAllPopup(animate: true).Subscribe();
// Using IPopupNavigation
MopupService.Instance.PopAllPopup(animate: true).Subscribe();Removes only the topmost popup from the popup navigation stack using a last-in, first-out (LIFO) approach.
this.Navigation.PopPopup(animate: true).Subscribe();Adds a new popup to the top of the popup navigation stack. The popup stack is maintained separately from the main MAUI navigation stack, allowing popups to be displayed as overlays.
var popup = new MyPopup { ViewModel = new MyViewModel() };
this.Navigation.PushPopup(popup, animate: true).Subscribe();Removes a specific popup page from anywhere in the popup stack, not just the topmost one.
this.Navigation.RemovePopupPage(specificPopup, animate: true).Subscribe();Fires before the popup is actually removed from the navigation stack, allowing for interception or logging.
MopupService.Instance.PoppingObservable()
.Subscribe(args =>
{
Console.WriteLine($"Popup {args} is about to be popped");
});Fires after the popup has been fully removed from the navigation stack, allowing for cleanup or tracking.
MopupService.Instance.PoppedObservable()
.Subscribe(args =>
{
Console.WriteLine($"Popup {args} has been popped");
});Fires before the popup is actually added to the navigation stack, allowing for interception or validation.
MopupService.Instance.PushingObservable()
.Subscribe(args =>
{
Console.WriteLine($"Popup {args} is about to be pushed");
});Fires after the popup has been fully added to the navigation stack, allowing for tracking or initialization.
MopupService.Instance.PushedObservable()
.Subscribe(args =>
{
Console.WriteLine($"Popup {args} has been pushed");
});A base popup page that implements IViewFor for use without a specific ViewModel type.
A strongly-typed popup page that implements IViewFor<TViewModel>, providing compile-time safety and IntelliSense support for your ViewModel.
The core team members, ReactiveUI contributors and contributors in the ecosystem do this open-source work in their free time. If you use ReactiveUI, a serious task, and you'd like us to invest more time on it, please donate. This project increases your income/productivity too. It makes development and applications faster and it reduces the required bandwidth.
This is how we use the donations:
- Allow the core team to work on ReactiveUI
- Thank contributors if they invested a large amount of time in contributing
- Support projects in the ecosystem
ReactiveUI is developed and maintained by the ReactiveUI community and contributors. This project is part of the broader ReactiveUI ecosystem, which provides a composable, testable framework for building reactive applications across multiple platforms.