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); } ///