Skip to content

Fix shell navigation, Cancel canExecute, AOT-safe FromEventPattern, and MAUI TFMs in sample projects#4321

Merged
glennawatson merged 2 commits intocleanup/remove-old-integration-testsfrom
copilot/fix-shell-navigation-issues
Apr 2, 2026
Merged

Fix shell navigation, Cancel canExecute, AOT-safe FromEventPattern, and MAUI TFMs in sample projects#4321
glennawatson merged 2 commits intocleanup/remove-old-integration-testsfrom
copilot/fix-shell-navigation-issues

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

Addresses all open review comments on #4319. The MAUI sample used NavigationPage while calling Shell.Current?.DisplayAlert(...), guaranteeing a null reference and silent no-op alerts. The Cancel command in all three sample ViewModels was always enabled, contradicting its doc comment. The WPF PasswordChanged binding used reflection-based FromEventPattern, and the MAUI project targeted net10.0 instead of deployable platform TFMs.

MAUI: Shell navigation

  • Added AppShell.xaml / AppShell.xaml.cs; App.xaml.cs now creates new AppShell() instead of new NavigationPage(new LoginPage())

MAUI: Alert handling — no more async void

Replaced the async void subscribe + Shell.Current?.DisplayAlert pattern with a proper Rx chain using the ContentPage instance method:

ViewModel.Login
    .SelectMany(success => Observable.FromAsync(() =>
        DisplayAlert(
            success ? "Login Successful" : "Login Failed",
            success ? "Welcome!" : "Invalid credentials.",
            "OK")))
    .Subscribe()
    .DisposeWith(d);

All three ViewModels: Cancel respects Login.IsExecuting

A Subject<Unit> breaks the circular dependency (Login needs a cancel signal; Cancel needs Login.IsExecuting):

var cancelSubject = new Subject<Unit>();

Login = ReactiveCommand.CreateFromObservable(
    () => Observable.Return(Password is "secret")
        .Delay(TimeSpan.FromSeconds(1), scheduler)
        .TakeUntil(cancelSubject),
    canLogin, scheduler);

Cancel = ReactiveCommand.Create(
    () => cancelSubject.OnNext(Unit.Default),
    Login.IsExecuting, scheduler);

WPF: AOT-safe FromEventPattern

Swapped the reflection overload for the strongly-typed delegate overload:

Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(
    h => Password.PasswordChanged += h,
    h => Password.PasswordChanged -= h)

MAUI csproj: proper platform TFMs

Replaced <TargetFramework>net10.0</TargetFramework> with platform-specific TFMs (net10.0-android; net10.0-ios/net10.0-maccatalyst on macOS/Windows; net10.0-windows10.0.19041.0 on Windows), added <OutputType>Exe</OutputType>, SupportedOSPlatformVersion, and Windows packaging properties.

…T-safe FromEventPattern, MAUI TFMs

- Add AppShell.xaml + AppShell.xaml.cs for MAUI Shell navigation
- Update App.xaml.cs to use new AppShell() instead of NavigationPage
- Fix LoginPage.xaml.cs: replace async void Subscribe with SelectMany/FromAsync + instance DisplayAlert
- Fix all three LoginViewModel.cs: Cancel uses Login.IsExecuting as canExecute via Subject<Unit> pattern
- Fix WPF LoginView.xaml.cs: use strongly-typed Observable.FromEventPattern overload
- Fix MAUI csproj: add proper platform-specific MAUI TFMs + OutputType=Exe

Agent-Logs-Url: https://github.com/reactiveui/ReactiveUI/sessions/93dee6e0-8644-4b85-996e-8a19bd5ccf27

Co-authored-by: glennawatson <5834289+glennawatson@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix shell navigation issues and adapt for consistency Fix shell navigation, Cancel canExecute, AOT-safe FromEventPattern, and MAUI TFMs in sample projects Apr 2, 2026
Copilot AI requested a review from glennawatson April 2, 2026 12:15
@glennawatson glennawatson marked this pull request as ready for review April 2, 2026 23:11
@glennawatson glennawatson merged commit 344214b into cleanup/remove-old-integration-tests Apr 2, 2026
@glennawatson glennawatson deleted the copilot/fix-shell-navigation-issues branch April 2, 2026 23:11
glennawatson added a commit that referenced this pull request Apr 3, 2026
…nd MAUI TFMs in sample projects (#4321)

Addresses all open review comments on #4319. The MAUI sample used
`NavigationPage` while calling `Shell.Current?.DisplayAlert(...)`,
guaranteeing a null reference and silent no-op alerts. The `Cancel`
command in all three sample ViewModels was always enabled, contradicting
its doc comment. The WPF `PasswordChanged` binding used reflection-based
`FromEventPattern`, and the MAUI project targeted `net10.0` instead of
deployable platform TFMs.

## MAUI: Shell navigation
- Added `AppShell.xaml` / `AppShell.xaml.cs`; `App.xaml.cs` now creates
`new AppShell()` instead of `new NavigationPage(new LoginPage())`

## MAUI: Alert handling — no more `async void`
Replaced the `async void` subscribe + `Shell.Current?.DisplayAlert`
pattern with a proper Rx chain using the `ContentPage` instance method:
```csharp
ViewModel.Login
    .SelectMany(success => Observable.FromAsync(() =>
        DisplayAlert(
            success ? "Login Successful" : "Login Failed",
            success ? "Welcome!" : "Invalid credentials.",
            "OK")))
    .Subscribe()
    .DisposeWith(d);
```

## All three ViewModels: `Cancel` respects `Login.IsExecuting`
A `Subject<Unit>` breaks the circular dependency (`Login` needs a cancel
signal; `Cancel` needs `Login.IsExecuting`):
```csharp
var cancelSubject = new Subject<Unit>();

Login = ReactiveCommand.CreateFromObservable(
    () => Observable.Return(Password is "secret")
        .Delay(TimeSpan.FromSeconds(1), scheduler)
        .TakeUntil(cancelSubject),
    canLogin, scheduler);

Cancel = ReactiveCommand.Create(
    () => cancelSubject.OnNext(Unit.Default),
    Login.IsExecuting, scheduler);
```

## WPF: AOT-safe `FromEventPattern`
Swapped the reflection overload for the strongly-typed delegate
overload:
```csharp
Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(
    h => Password.PasswordChanged += h,
    h => Password.PasswordChanged -= h)
```

## MAUI csproj: proper platform TFMs
Replaced `<TargetFramework>net10.0</TargetFramework>` with
platform-specific TFMs (`net10.0-android`;
`net10.0-ios`/`net10.0-maccatalyst` on macOS/Windows;
`net10.0-windows10.0.19041.0` on Windows), added
`<OutputType>Exe</OutputType>`, `SupportedOSPlatformVersion`, and
Windows packaging properties.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: glennawatson <5834289+glennawatson@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Apr 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants