From 905e0a7a2e6a17e6e65d5906883c5689eac3635e Mon Sep 17 00:00:00 2001 From: Michael Katsoulakis Date: Fri, 26 Jun 2026 10:22:57 +0100 Subject: [PATCH] feat(extract): link XAML views to ViewModels and extract binding references (#1473) Builds on the initial XAML support (#1460). Resolves a view to its ViewModel from an explicit , a design-time d:DataContext="{d:DesignInstance Type=...}", the View->ViewModel naming convention, or Prism ViewModelLocator.AutoWireViewModel="True". Resolution is always against an actually-extracted C# class node, so a name matching no class (or an ambiguous 2+) emits no edge -- explicit DataContext is EXTRACTED, convention/Prism are INFERRED. Also extracts binding paths ({Binding User.Name}, Path=Order.Total), commands (Command="{Binding SaveCommand}"), converters, and CommunityToolkit [ObservableProperty]/[RelayCommand] generated members. The #1460 event-handler hardening is preserved unchanged: events still resolve only to methods with a .NET (object sender, ...EventArgs e) signature, and the free-form-attribute denylist still prevents values like Content="Save" from fabricating event edges (both regression tests still pass). ViewModel discovery is bounded to the active extraction root. Ported from PR #1473 by @MikeKatsoulakis (clean 3-way merge onto current v8). Maintainer fix on top: the CommunityToolkit member reader now reads the code-behind with errors="replace", so a non-UTF8 ViewModel .cs can't raise UnicodeDecodeError and abort extract_xaml (matches every other reader in the module). Added a regression test for that case. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 1 + graphify/extract.py | 374 +++++++++++++++++- tests/fixtures/bindings.xaml | 12 + tests/fixtures/xaml_viewmodel/App.csproj | 5 + .../ViewModels/DesignViewModel.cs | 6 + .../ViewModels/MainViewModel.cs | 6 + .../ViewModels/PrismOrderViewModel.cs | 5 + .../ViewModels/SettingsViewModel.cs | 6 + .../ViewModels/ToolkitViewModel.cs | 30 ++ .../xaml_viewmodel/Views/DesignView.xaml | 8 + .../Views/ExplicitMainWindow.xaml | 9 + .../xaml_viewmodel/Views/PrismOrderView.xaml | 5 + .../xaml_viewmodel/Views/SettingsView.xaml | 5 + .../xaml_viewmodel/Views/ToolkitView.xaml | 19 + tests/test_dotnet.py | 221 ++++++++++- 15 files changed, 699 insertions(+), 13 deletions(-) create mode 100644 tests/fixtures/bindings.xaml create mode 100644 tests/fixtures/xaml_viewmodel/App.csproj create mode 100644 tests/fixtures/xaml_viewmodel/ViewModels/DesignViewModel.cs create mode 100644 tests/fixtures/xaml_viewmodel/ViewModels/MainViewModel.cs create mode 100644 tests/fixtures/xaml_viewmodel/ViewModels/PrismOrderViewModel.cs create mode 100644 tests/fixtures/xaml_viewmodel/ViewModels/SettingsViewModel.cs create mode 100644 tests/fixtures/xaml_viewmodel/ViewModels/ToolkitViewModel.cs create mode 100644 tests/fixtures/xaml_viewmodel/Views/DesignView.xaml create mode 100644 tests/fixtures/xaml_viewmodel/Views/ExplicitMainWindow.xaml create mode 100644 tests/fixtures/xaml_viewmodel/Views/PrismOrderView.xaml create mode 100644 tests/fixtures/xaml_viewmodel/Views/SettingsView.xaml create mode 100644 tests/fixtures/xaml_viewmodel/Views/ToolkitView.xaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 89670ab..5677e4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Full release notes with details on each version: [GitHub Releases](https://githu ## Unreleased +- Feat: link WPF/XAML views to their ViewModels and extract richer binding references (#1473, thanks @MikeKatsoulakis). Builds on the initial XAML support (#1460). Resolves a view to its ViewModel from an explicit ``, a design-time `d:DataContext="{d:DesignInstance Type=…}"`, the `View`→`ViewModel` naming convention, or Prism `ViewModelLocator.AutoWireViewModel="True"` — always against an actually-extracted C# class, so a name with no matching class (or an ambiguous one) emits no edge (explicit DataContext is EXTRACTED, conventions are INFERRED). Also extracts binding paths (`{Binding User.Name}`, `Path=Order.Total`), commands (`Command="{Binding SaveCommand}"`), converters, and CommunityToolkit `[ObservableProperty]`/`[RelayCommand]` generated members. The event-handler resolution stays gated on the .NET handler signature (no spurious event edges), and ViewModel discovery is bounded to the extraction root. - Fix: `.vue` Single File Components now extract their `