Skip to content

feat: follow WinForms host properties through ReactiveUI.Binding's ObservedProperty - #506

Merged
glennawatson merged 1 commit into
mainfrom
feat/hosts-observed-property
Sep 26, 2026
Merged

glennawatson merged 1 commit into
mainfrom
feat/hosts-observed-property

Conversation

@glennawatson

Copy link
Copy Markdown
Contributor

Summary

With ReactiveUI.Binding 8.4.0 or later, the WinForms hosts follow their own properties through Binding's ObservedProperty, with WhenAnyValue semantics.

  • The [RoutedControlHost] and [ViewModelControlHost] hosts use ObservedProperty when the project's ReactiveUI.Binding has it. Create replaces WhenAnyValue for DefaultContent, Content and ViewModel, and Then plus Switch replace WhenAnyObservable for Router.CurrentViewModel and ViewContractObservable, in the flavour the host's view API comes from.
  • With an older ReactiveUI.Binding, or none, the hosts keep following their own PropertyChanged. A new ControlHostAnalyzer reports RXUISG0022 (Info) on each such host.
  • No package dependency on ReactiveUI.Binding. The generator and the analyzer both decide from ViewApiRules, which looks for ObservedProperty in the referenced Binding flavour and now also picks the view API.

Why

Generated code has to keep WhenAny semantics without calling an API ReactiveUI.Binding intercepts.

  • Binding's generator cannot see call sites in another generator's output, so the 4.0.1 hosts moved to their own PropertyChanged following; ReactiveUI.Binding 8.4.0 adds ObservedProperty, a non-intercepted, AOT-safe observation API with WhenAnyValue's semantics, for exactly this case.
  • A package dependency would force one Binding flavour on every consumer, so the choice follows what the project references.

Breaking changes

None. Hosts on ReactiveUI.Binding 8.4.0 or later follow their properties through Binding's observation, including any registered observation provider; other hosts are generated as in 4.1.0.

How this was verified

New tests compile and run both hosts against the real ReactiveUI.Binding 8.4.0, lean and .Reactive, showing the default content and then the routed or hosted view; the analyzer is tested with and without ObservedProperty.

  • Scratch apps on a ReactiveUI build that depends on ReactiveUI.Binding, in both flavours, run the hosts with Binding 8.4.0 (generated through ObservedProperty) and with 8.1.0 (the fallback).

Notes for the reviewer

Start with ViewApiRules.cs and the three Append* members in ControlHostExtensions.cs.

  • ControlHostWriter is renamed ControlHostExtensions, as it now holds an extension block.
  • The tests reference ReactiveUI.Binding for its runtime types only; the test project removes Binding's generator and analyzer from the compiler, which ExcludeAssets alone did not.
  • No published ReactiveUI is built on ReactiveUI.Binding yet, so the host tests declare the few ReactiveUI types a host touches in source.

Checklist

  • I have read the Contribute guide
  • The PR title follows Conventional Commits
  • Tests cover this change, or the summary says why they do not
  • New or changed public API has XML documentation

…servedProperty

With ReactiveUI.Binding 8.4.0 or later, the [RoutedControlHost] and
[ViewModelControlHost] hosts follow DefaultContent, Content, ViewModel, Router
and ViewContractObservable through Binding's ObservedProperty, in the flavour
their view API comes from: Create for WhenAnyValue, and Then and Switch for
WhenAnyObservable, with static lambdas. ObservedProperty is not intercepted, so
it works from generated code, and keeps WhenAnyValue's semantics.

With an older ReactiveUI.Binding, or none, the hosts keep following their own
PropertyChanged, and the new ControlHostAnalyzer reports RXUISG0022 (Info) on
each host. The generator and the analyzer decide from the same ViewApiRules file,
which now also picks the view API. There is no package dependency on Binding:
the decision follows the Binding the project references.
@codecov

codecov Bot commented Sep 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.75%. Comparing base (7158aa0) to head (344714a).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #506      +/-   ##
==========================================
+ Coverage   96.70%   96.75%   +0.05%     
==========================================
  Files          74       76       +2     
  Lines        3759     3824      +65     
  Branches      531      544      +13     
==========================================
+ Hits         3635     3700      +65     
  Misses        124      124              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@glennawatson
glennawatson merged commit c8a8c38 into main Sep 26, 2026
12 checks passed
@glennawatson
glennawatson deleted the feat/hosts-observed-property branch September 26, 2026 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant