diff --git a/src/examples/ReactiveUI.Samples.Maui/App.xaml.cs b/src/examples/ReactiveUI.Samples.Maui/App.xaml.cs
index 6b504ceeef..cd02bfa76b 100644
--- a/src/examples/ReactiveUI.Samples.Maui/App.xaml.cs
+++ b/src/examples/ReactiveUI.Samples.Maui/App.xaml.cs
@@ -17,5 +17,5 @@ public partial class App : Application
///
protected override Window CreateWindow(IActivationState? activationState) =>
- new(new NavigationPage(new LoginPage()));
+ new(new AppShell());
}
diff --git a/src/examples/ReactiveUI.Samples.Maui/AppShell.xaml b/src/examples/ReactiveUI.Samples.Maui/AppShell.xaml
new file mode 100644
index 0000000000..d9c75b93e5
--- /dev/null
+++ b/src/examples/ReactiveUI.Samples.Maui/AppShell.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/src/examples/ReactiveUI.Samples.Maui/AppShell.xaml.cs b/src/examples/ReactiveUI.Samples.Maui/AppShell.xaml.cs
new file mode 100644
index 0000000000..c1c67613c9
--- /dev/null
+++ b/src/examples/ReactiveUI.Samples.Maui/AppShell.xaml.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved.
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+namespace ReactiveUI.Samples.Maui;
+
+///
+/// Application shell providing Shell navigation for the MAUI sample.
+///
+public partial class AppShell : Shell
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AppShell() => InitializeComponent();
+}
diff --git a/src/examples/ReactiveUI.Samples.Maui/LoginPage.xaml.cs b/src/examples/ReactiveUI.Samples.Maui/LoginPage.xaml.cs
index 83289d6eec..52dcc75594 100644
--- a/src/examples/ReactiveUI.Samples.Maui/LoginPage.xaml.cs
+++ b/src/examples/ReactiveUI.Samples.Maui/LoginPage.xaml.cs
@@ -34,11 +34,12 @@ public LoginPage()
.DisposeWith(d);
ViewModel.Login
- .Subscribe(async success =>
- await (Shell.Current?.DisplayAlert(
+ .SelectMany(success => Observable.FromAsync(() =>
+ DisplayAlert(
success ? "Login Successful" : "Login Failed",
success ? "Welcome!" : "Invalid credentials.",
- "OK") ?? Task.CompletedTask))
+ "OK")))
+ .Subscribe()
.DisposeWith(d);
});
}
diff --git a/src/examples/ReactiveUI.Samples.Maui/LoginViewModel.cs b/src/examples/ReactiveUI.Samples.Maui/LoginViewModel.cs
index 96ca190e22..6b769c05ae 100644
--- a/src/examples/ReactiveUI.Samples.Maui/LoginViewModel.cs
+++ b/src/examples/ReactiveUI.Samples.Maui/LoginViewModel.cs
@@ -4,6 +4,7 @@
// See the LICENSE file in the project root for full license information.
using System.Reactive.Concurrency;
+using System.Reactive.Subjects;
namespace ReactiveUI.Samples.Maui;
@@ -26,15 +27,20 @@ public LoginViewModel(IScheduler scheduler)
vm => vm.Password,
(user, pass) => !string.IsNullOrWhiteSpace(user) && !string.IsNullOrWhiteSpace(pass));
- Cancel = ReactiveCommand.Create(() => { }, outputScheduler: scheduler);
+ var cancelSubject = new Subject();
Login = ReactiveCommand.CreateFromObservable(
() => Observable
.Return(Password is "secret")
.Delay(TimeSpan.FromSeconds(1), scheduler)
- .TakeUntil(Cancel),
+ .TakeUntil(cancelSubject),
canLogin,
scheduler);
+
+ Cancel = ReactiveCommand.Create(
+ () => cancelSubject.OnNext(Unit.Default),
+ Login.IsExecuting,
+ scheduler);
}
///
diff --git a/src/examples/ReactiveUI.Samples.Maui/ReactiveUI.Samples.Maui.csproj b/src/examples/ReactiveUI.Samples.Maui/ReactiveUI.Samples.Maui.csproj
index 50e2ccf2cd..98f8dd9e15 100644
--- a/src/examples/ReactiveUI.Samples.Maui/ReactiveUI.Samples.Maui.csproj
+++ b/src/examples/ReactiveUI.Samples.Maui/ReactiveUI.Samples.Maui.csproj
@@ -1,7 +1,10 @@
- net10.0
+ net10.0-android
+ $(TargetFrameworks);net10.0-ios;net10.0-maccatalyst
+ $(TargetFrameworks);net10.0-windows10.0.19041.0
+ Exe
true
enable
enable
@@ -10,6 +13,18 @@
false
false
false
+ 15.0
+ 15.0
+ 24.0
+ 10.0.19041.0
+ 10.0.19041.0
+
+
+
+ None
+ false
+ false
+ false
diff --git a/src/examples/ReactiveUI.Samples.Winforms/LoginViewModel.cs b/src/examples/ReactiveUI.Samples.Winforms/LoginViewModel.cs
index f2561654d5..2339746656 100644
--- a/src/examples/ReactiveUI.Samples.Winforms/LoginViewModel.cs
+++ b/src/examples/ReactiveUI.Samples.Winforms/LoginViewModel.cs
@@ -4,6 +4,7 @@
// See the LICENSE file in the project root for full license information.
using System.Reactive.Concurrency;
+using System.Reactive.Subjects;
namespace ReactiveUI.Samples.Winforms;
@@ -26,15 +27,20 @@ public LoginViewModel(IScheduler scheduler)
vm => vm.Password,
(user, pass) => !string.IsNullOrWhiteSpace(user) && !string.IsNullOrWhiteSpace(pass));
- Cancel = ReactiveCommand.Create(() => { }, outputScheduler: scheduler);
+ var cancelSubject = new Subject();
Login = ReactiveCommand.CreateFromObservable(
() => Observable
.Return(Password is "secret")
.Delay(TimeSpan.FromSeconds(1), scheduler)
- .TakeUntil(Cancel),
+ .TakeUntil(cancelSubject),
canLogin,
scheduler);
+
+ Cancel = ReactiveCommand.Create(
+ () => cancelSubject.OnNext(Unit.Default),
+ Login.IsExecuting,
+ scheduler);
}
///
diff --git a/src/examples/ReactiveUI.Samples.Wpf/LoginView.xaml.cs b/src/examples/ReactiveUI.Samples.Wpf/LoginView.xaml.cs
index 48016a4fc1..8d7cdbfbf7 100644
--- a/src/examples/ReactiveUI.Samples.Wpf/LoginView.xaml.cs
+++ b/src/examples/ReactiveUI.Samples.Wpf/LoginView.xaml.cs
@@ -34,7 +34,9 @@ public LoginView()
.DisposeWith(d);
// WPF PasswordBox doesn't support data binding, so marshal changes manually.
- Observable.FromEventPattern(Password, nameof(PasswordBox.PasswordChanged))
+ Observable.FromEventPattern(
+ h => Password.PasswordChanged += h,
+ h => Password.PasswordChanged -= h)
.Select(_ => Password.Password)
.BindTo(this, v => v.ViewModel!.Password)
.DisposeWith(d);
diff --git a/src/examples/ReactiveUI.Samples.Wpf/LoginViewModel.cs b/src/examples/ReactiveUI.Samples.Wpf/LoginViewModel.cs
index 84802416e7..cd1b738e81 100644
--- a/src/examples/ReactiveUI.Samples.Wpf/LoginViewModel.cs
+++ b/src/examples/ReactiveUI.Samples.Wpf/LoginViewModel.cs
@@ -4,6 +4,7 @@
// See the LICENSE file in the project root for full license information.
using System.Reactive.Concurrency;
+using System.Reactive.Subjects;
namespace ReactiveUI.Samples.Wpf;
@@ -26,15 +27,20 @@ public LoginViewModel(IScheduler scheduler)
vm => vm.Password,
(user, pass) => !string.IsNullOrWhiteSpace(user) && !string.IsNullOrWhiteSpace(pass));
- Cancel = ReactiveCommand.Create(() => { }, outputScheduler: scheduler);
+ var cancelSubject = new Subject();
Login = ReactiveCommand.CreateFromObservable(
() => Observable
.Return(Password is "secret")
.Delay(TimeSpan.FromSeconds(1), scheduler)
- .TakeUntil(Cancel),
+ .TakeUntil(cancelSubject),
canLogin,
scheduler);
+
+ Cancel = ReactiveCommand.Create(
+ () => cancelSubject.OnNext(Unit.Default),
+ Login.IsExecuting,
+ scheduler);
}
///