From 7ab05c60fdf5c2bbe86471255d99d6efe85c4496 Mon Sep 17 00:00:00 2001 From: Kazuki Ota <117221407+kaota_microsoft@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:47:56 +0900 Subject: [PATCH] Add WPF/XAML binding audit guidance to R3 migration skill Adds a dedicated step to audit WPF/XAML .Value bindings after the mechanical rewrite, and clarifies in rules.json that XAML-bound read/write properties should use BindableReactiveProperty and read-only ones should use ToReadOnlyBindableReactiveProperty(...) exposed as IReadOnlyBindableReactiveProperty. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../migrating-reactiveproperty-to-r3/SKILL.md | 19 ++++++++++++++++--- .../references/rules.json | 10 +++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/skills/migrating-reactiveproperty-to-r3/SKILL.md b/skills/migrating-reactiveproperty-to-r3/SKILL.md index 9a26353c..05f24958 100644 --- a/skills/migrating-reactiveproperty-to-r3/SKILL.md +++ b/skills/migrating-reactiveproperty-to-r3/SKILL.md @@ -1,4 +1,4 @@ ---- +--- name: migrating-reactiveproperty-to-r3 description: 'Migrate an application from ReactiveProperty (namespace Reactive.Bindings) to R3 plus the ReactiveProperty.R3 bridge package. Use this when asked to "migrate to R3", "move off ReactiveProperty", "replace ReactiveProperty with R3", "swap Reactive.Bindings for R3", or to rewrite ViewModels/commands/validation/notifiers/collections that use ReactiveProperty so they run on R3. Drives the rewrite from a mapping table (references/rules.json): everything R3 already provides becomes native R3, every genuine gap becomes a ReactiveProperty.R3 type, and the few cases that cannot be rewritten mechanically are flagged for manual review.' --- @@ -71,7 +71,7 @@ rule has these fields and nothing else: For every ReactiveProperty symbol in the project, find its rule in `references/rules.json` and apply it: - `r3-direct` → rewrite to the R3 `replacement`. - `reactiveproperty-r3` → rewrite to the `ReactiveProperty.R3` `replacement`. -- `manualReview` → leave it and record it for step 4. +- `manualReview` → leave it and record it for step 5. Generic type rules apply to the closed forms too — e.g. the rule for `Reactive.Bindings.ReactivePropertySlim` covers `ReactivePropertySlim`. @@ -84,7 +84,20 @@ Generic type rules apply to the closed forms too — e.g. the rule for …). When in doubt, add `using R3;` to files that touch any rewritten symbol. - `Reactive.Bindings*` namespaces map to `R3` and/or `Reactive.Bindings.R3*` per the rules. -### 4. Build, test, and report +### 4. Audit WPF/XAML bindings +- In WPF projects, search XAML for `{Binding Xxx.Value}` and `ItemsSource="{Binding Xxx.Value}"` after + the mechanical rewrite. Any `Xxx` property that is bound through `.Value` must raise WPF + `PropertyChanged` for `Value` changes. +- For UI-bound read/write properties, prefer `BindableReactiveProperty` instead of + `ReactiveProperty` even when the original type was `ReactivePropertySlim`. +- For UI-bound read-only properties, create them with `ToReadOnlyBindableReactiveProperty(...)` and + expose them as `IReadOnlyBindableReactiveProperty`; do **not** expose the internal concrete + `ReadOnlyBindableReactiveProperty` type. Use `ReadOnlyReactiveProperty` only for non-XAML + code paths. +- Build success alone is not enough for WPF migration: verify every XAML `.Value` binding is backed by + a bindable R3 property or interface. + +### 5. Build, test, and report - Build the project and run its **existing** tests. - Report: build status, test status, and the **manual-review list** — each `manualReview` hit with its file, line, and the rule's `manualReview` note. Keep this list concise; it is the only narrative diff --git a/skills/migrating-reactiveproperty-to-r3/references/rules.json b/skills/migrating-reactiveproperty-to-r3/references/rules.json index 908df34f..89a70c7d 100644 --- a/skills/migrating-reactiveproperty-to-r3/references/rules.json +++ b/skills/migrating-reactiveproperty-to-r3/references/rules.json @@ -1,4 +1,4 @@ -{ +{ "schemaVersion": 2, "rules": [ { @@ -21,7 +21,7 @@ "usingAdd": ["R3"], "usingRemove": ["Reactive.Bindings"], "manualReview": null, - "notes": "R3 ReactiveProperty is the read/write slim property; same Value/IObservable semantics." + "notes": "R3 ReactiveProperty is the read/write slim property; same Value/IObservable semantics. In WPF, if this property is bound from XAML through `.Value`, prefer R3 BindableReactiveProperty so Value changes raise PropertyChanged for binding updates." }, { "ruleId": "RP-READONLY-PROP-SLIM", @@ -32,7 +32,7 @@ "usingAdd": ["R3"], "usingRemove": ["Reactive.Bindings"], "manualReview": null, - "notes": "R3 ReadOnlyReactiveProperty is the slim read-only property." + "notes": "R3 ReadOnlyReactiveProperty is the slim read-only property for non-XAML code paths. In WPF, if this property is bound from XAML through `.Value` (including ItemsSource), create it with ToReadOnlyBindableReactiveProperty(...) and expose the public property as IReadOnlyBindableReactiveProperty so Value changes raise PropertyChanged." }, { "ruleId": "RP-PROP-BINDING", @@ -54,7 +54,7 @@ "usingAdd": ["R3"], "usingRemove": ["Reactive.Bindings"], "manualReview": null, - "notes": "Use R3 BindableReactiveProperty instead when the value is bound to XAML and must raise PropertyChanged." + "notes": "Use R3 BindableReactiveProperty instead when the value is bound to XAML and must raise PropertyChanged. For read-only XAML-bound values, use ToReadOnlyBindableReactiveProperty(...) and expose IReadOnlyBindableReactiveProperty." }, { "ruleId": "RP-COMMAND", @@ -98,7 +98,7 @@ "usingAdd": ["R3"], "usingRemove": ["Reactive.Bindings"], "manualReview": null, - "notes": "Build a read-only property from an IObservable/Observable source." + "notes": "Build a read-only property from an IObservable/Observable source. In WPF, if the result is bound from XAML through `.Value` (including ItemsSource), use ToReadOnlyBindableReactiveProperty(...) instead and expose IReadOnlyBindableReactiveProperty." }, { "ruleId": "RP-TO-READONLY-PROP",