diff --git a/CLAUDE.md b/CLAUDE.md index c99910c4..f452b4e4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This document provides guidance for AI assistants and contributors working in th ## Overview -ReactiveUI.SourceGenerators is a Roslyn incremental source-generator package that automates ReactiveUI boilerplate at compile-time. It generates reactive properties, reactive commands, `IViewFor` implementations, WinForms view hosts, bindable derived lists, reactive collections, and full reactive-object scaffolding — all with zero runtime reflection, making generated code fully AOT-compatible. +ReactiveUI.SourceGenerators is a Roslyn incremental source-generator package that automates ReactiveUI boilerplate at compile-time. It generates reactive properties, reactive commands, WinForms view hosts, bindable derived lists, reactive collections, and full reactive-object scaffolding — all with zero runtime reflection, making generated code fully AOT-compatible. **Minimum consumer requirements:** C# 12.0 · Visual Studio 17.8.0 · ReactiveUI 23.2.28+ @@ -40,7 +40,6 @@ src/ │ ├── AttributeDefinitions.cs # Injected attribute source texts │ ├── Reactive/ # [Reactive] generator + Execute + models │ ├── ReactiveCommand/ # [ReactiveCommand] generator + Execute + models -│ ├── IViewFor/ # [IViewFor] generator + Execute + models │ ├── RoutedControlHost/ # [RoutedControlHost] generator │ ├── ViewModelControlHost/ # [ViewModelControlHost] generator │ ├── BindableDerivedList/ # [BindableDerivedList] generator @@ -125,7 +124,7 @@ Initialize() **Fast paths:** - Find attributed targets with `ForAttributeWithMetadataName`; benchmarks show it allocates less than a syntax - provider, cold and incrementally. A generic attribute is registered by its arity name (``IViewForAttribute`1``). + provider, cold and incrementally. A generic attribute is registered by its arity name (``NameAttribute`1``). - Reject nodes in the syntax predicate before any binding: node kind, parent shape, `partial` modifier. - In the transform, read `context.Attributes[0]` rather than searching the symbol's attributes again, and ask for semantic information only when syntax says it can exist (documentation only when the node has a doc comment). @@ -143,15 +142,14 @@ Initialize() |-----------------|-----------|--------------| | `ReactiveGenerator` | `[Reactive]` | Field (all Roslyn) or partial property (ROSYLN_412+) | | `ReactiveCommandGenerator` | `[ReactiveCommand]` | Method | -| `IViewForGenerator` | `[IViewFor]` | Class (the `IViewFor` implementation; view registration belongs to ReactiveUI.Binding) | | `RoutedControlHostGenerator` | `[RoutedControlHost]` | Class | | `ViewModelControlHostGenerator` | `[ViewModelControlHost]` | Class | | `BindableDerivedListGenerator` | `[BindableDerivedList]` | Field (`ReadOnlyObservableCollection`) | | `ReactiveCollectionGenerator` | `[ReactiveCollection]` | Field (`ObservableCollection`) | | `ReactiveObjectGenerator` | `[IReactiveObject]` | Class | -`[ObservableAsProperty]` and IViewFor view registration moved to ReactiveUI.Binding (ReactiveUI's binding engine) and -were removed here. Do not reintroduce features ReactiveUI.Binding provides. +`[ObservableAsProperty]`, IViewFor view registration and `[IViewFor]` were removed here in favour of ReactiveUI.Binding +(ReactiveUI's binding engine). Do not reintroduce features ReactiveUI.Binding provides. ## Analyzers & Suppressors @@ -192,7 +190,7 @@ The test project multi-targets `net8.0;net9.0;net10.0` (controlled by `$(TestTfm ### Snapshot tests Generator tests extend `TestBase` and call `TestHelper.TestPass(sourceCode)`. Each generated file is stored -as `{FOLDER}/{class}.{method}#{hint}.verified.cs` in the generator's folder (`REACTIVE/`, `REACTIVECMD/`, `IVIEWFOR/`, +as `{FOLDER}/{class}.{method}#{hint}.verified.cs` in the generator's folder (`REACTIVE/`, `REACTIVECMD/`, `DERIVEDLIST/`, `REACTIVECOLL/`, `REACTIVEOBJ/`). The class segment is the test class's capitals, and common words in the method and hint segments are abbreviated, so every path stays well inside the Windows path limit. A mismatch writes `*.received.cs` beside the snapshot and fails; a snapshot the run no longer produces also fails. Generated-code diff --git a/README.md b/README.md index df1ab672..cba991db 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ This documentation covers using ReactiveUI Source Generators to simplify and enh - [Detailed Usage](#welcome-to-a-new-way---source-generators) - [Reactive](#reactive) - [ReactiveCommand](#reactivecommand) - - [IViewFor](#iviewfor) - [BindableDerivedList](#usage-readonlyobservablecollection) - [Platform-Specific Attributes](#platform-specific-attributes) - [Moved to ReactiveUI.Binding](#moved-to-reactiveuibinding) @@ -47,16 +46,13 @@ ReactiveUI Source Generators automatically generate ReactiveUI objects to stream - `[ReactiveCommand(OutputScheduler = nameof(_isheduler))]` using a Scheduler defined in the class - `[ReactiveCommand][property: AttributeToAddToCommand]` with Attribute passthrough - `[ReactiveCommand(AccessModifier = PropertyAccessModifier.Internal)]` sets the access modifier of the generated command property -- `[IViewFor(nameof(ViewModelName))]` -- `[IViewFor]` -- `[IViewFor("YourNameSpace.YourGenericViewModel")]` Generic - `[RoutedControlHost("YourNameSpace.CustomControl")]` - `[ViewModelControlHost("YourNameSpace.CustomControl")]` - `[BindableDerivedList]` Generates a derived list from a ReadOnlyObservableCollection backing field - `[ReactiveCollection]` Generates property changed notifications on add, remove, new actions on a ObservableCollection backing field - `[IReactiveObject]` Generates IReactiveObject implementation for classes not able to inherit from ReactiveObject -`[ObservableAsProperty]` and IViewFor view registration moved to ReactiveUI.Binding; see [Moved to ReactiveUI.Binding](#moved-to-reactiveuibinding). +`[ObservableAsProperty]`, view registration and `[IViewFor]` have been removed in favour of ReactiveUI.Binding; see [Moved to ReactiveUI.Binding](#moved-to-reactiveuibinding). ### Compatibility Notes - For **.NET Framework 4.8 and older**, add [Polyfill by Simon Cropp](https://github.com/SimonCropp/Polyfill) or [PolySharp by Sergio Pedri](https://github.com/Sergio0694/PolySharp) to your project and set the `LangVersion` to 12.0 or later in your project file. @@ -93,9 +89,6 @@ Marks properties as reactive, generating getter and setter code. ### `[ReactiveCommand]` Generates commands, with options to add attributes or enable `CanExecute` functionality. -### `[IViewFor]` -Links a view to a view model for data binding. Supports generic types. - ### `[RoutedControlHost]` and `[ViewModelControlHost]` Platform-specific attributes for control hosting in WinForms applications. @@ -228,19 +221,17 @@ Define types with `[Reactive]` properties in assembly `A`, and then define the ## Moved to ReactiveUI.Binding ReactiveUI's binding engine, [ReactiveUI.Binding](https://github.com/reactiveui/ReactiveUI.Binding.SourceGenerators), -now provides two features this package used to generate. They have been removed from ReactiveUI.SourceGenerators. +now covers three features this package used to generate. They have been removed from ReactiveUI.SourceGenerators. | Removed | Use instead | | --- | --- | | `[ObservableAsProperty]` on a field, method, observable property or partial property, and the generated `InitializeOAPH()` | ReactiveUI.Binding's `[ObservableAsProperty]` on a `partial` property, assigned with `ToProperty` | | `RegisterViewsForViewModelsSourceGenerated()`, the `RegistrationType` and `ViewModelRegistrationType` options of `[IViewFor]`, and `SplatRegistrationType` | ReactiveUI.Binding's view locator, which registers views at compile time | +| `[IViewFor]` and `[IViewFor("...")]`, which generated the `ViewModel` property and the `IViewFor` implementation | Implement `IViewFor` on the view yourself, or derive from one of ReactiveUI's view base classes; ReactiveUI.Binding's view locator registers it | | RXUISG0014, RXUISG0017 and the RXUISPR0002 suppression | Nothing: they only applied to `[ObservableAsProperty]` | -`[IViewFor]` still generates the `ViewModel` property and the `IViewFor` implementation for each UI platform. It -implements the `IViewFor` of the ReactiveUI the project references: ReactiveUI's own on releases not built on -ReactiveUI.Binding, otherwise `ReactiveUI.Binding.IViewFor`, or `ReactiveUI.Binding.Reactive.IViewFor` with -ReactiveUI.Reactive. The interface is named in full, so no `using` for ReactiveUI.Binding is needed. The Windows Forms -`[RoutedControlHost]` and `[ViewModelControlHost]` resolve views through the matching view locator. +The Windows Forms `[RoutedControlHost]` and `[ViewModelControlHost]` remain. They resolve views through ReactiveUI's +view locator, or through ReactiveUI.Binding's when the project's ReactiveUI is built on it. The ReactiveUI.Binding replacements need a ReactiveUI release built on ReactiveUI.Binding. ReactiveUI 24.3 and earlier are not: there, ReactiveUI's own `ObservableAsPropertyHelper` and `IViewFor` are the ones in use, so @@ -266,6 +257,9 @@ public partial class MyReactiveClass : ReactiveObject } ``` +ReactiveUI.Binding 7.11 and later report `[ObservableAsProperty]` on a field, method or observable property +(RXUIBIND018), and its code fix rewrites each as a partial property. + Without ReactiveUI.Binding, or on ReactiveUI 24.3 and earlier, write the helper with ReactiveUI's `ToProperty`: ```csharp @@ -282,20 +276,24 @@ public class MyReactiveClass : ReactiveObject } ``` -### Registering views +### Views -ReactiveUI.Binding's view locator registers every class whose declaration implements `IViewFor`. Source generators -cannot see each other's output, so the view locator does not see the interface `[IViewFor]` adds. List the interface -on the class as well; the generated members still implement it: +`[IViewFor]` generated a view's `ViewModel` property and its `IViewFor` implementation. Write them on the view, or +derive the view from one of ReactiveUI's view base classes such as `ReactiveUserControl`: ```csharp -[IViewFor] +using ReactiveUI; + public partial class LoginView : UserControl, IViewFor { + public LoginViewModel? ViewModel { get; set; } + + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (LoginViewModel?)value; } } ``` -Without ReactiveUI.Binding, or on ReactiveUI 24.3 and earlier, register each view with Splat: +ReactiveUI.Binding's view locator registers every class whose declaration implements `IViewFor`. Without +ReactiveUI.Binding, or on ReactiveUI 24.3 and earlier, register each view with Splat: ```csharp AppLocator.CurrentMutable.Register>(static () => new LoginView()); @@ -487,51 +485,6 @@ public partial class MyReactiveClass } ``` -## Usage IViewFor `[IViewFor(nameof(ViewModelName))]` - -### IViewFor usage - -IViewFor is used to link a View to a ViewModel, this is used to link the ViewModel to the View in a way that ReactiveUI can use it to bind the ViewModel to the View. -The ViewModel is passed as a type to the IViewFor Attribute using generics. -The class must inherit from a UI Control from any of the following platforms and namespaces: -- Maui (Microsoft.Maui) -- WinUI (Microsoft.UI.Xaml) -- WPF (System.Windows or System.Windows.Controls) -- WinForms (System.Windows.Forms) -- Avalonia (Avalonia) -- Uno (Windows.UI.Xaml). - -### Usage IViewFor with ViewModel Name - Generic Types should be used with the fully qualified name, otherwise use nameof(ViewModelTypeName) -```csharp -using ReactiveUI.SourceGenerators; - -[IViewFor("MyReactiveGenericClass")] -public partial class MyReactiveControl : UserControl -{ - public MyReactiveControl() - { - InitializeComponent(); - ViewModel = new MyReactiveClass(); - } -} -``` - -### Usage IViewFor with ViewModel Type - -```csharp -using ReactiveUI.SourceGenerators; - -[IViewFor] -public partial class MyReactiveControl : UserControl -{ - public MyReactiveControl() - { - InitializeComponent(); - ViewModel = new MyReactiveClass(); - } -} -``` - ### Usage ReadOnlyObservableCollection ```csharp diff --git a/src/ReactiveUI.SourceGenerator.Tests/GeneratorSnapshot.cs b/src/ReactiveUI.SourceGenerator.Tests/GeneratorSnapshot.cs index f5e80784..01d4d0af 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/GeneratorSnapshot.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/GeneratorSnapshot.cs @@ -77,7 +77,6 @@ private static readonly (string Word, string Abbreviation)[] Abbreviations = ("Namespace", "Ns"), ("Multiple", "Multi"), ("ViewModel", "Vm"), - ("IViewFor", "Ivf"), ("Internal", "Int"), ("Inheritance", "Inh"), ("Modifier", "Mod"), diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/IVFG.Basic#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/IVFG.Basic#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/IVFG.Basic#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/IVFG.Basic#TestNs.TestViewWpf.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/IVFG.Basic#TestNs.TestViewWpf.Ivf.verified.cs deleted file mode 100644 index 438ecd1c..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/IVFG.Basic#TestNs.TestViewWpf.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.TestViewWpf.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the TestViewWpf which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class TestViewWpf : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.TestViewModel), typeof(TestViewWpf), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.TestViewModel BindingRoot => ViewModel; - - /// - public TestNs.TestViewModel ViewModel { get => (TestNs.TestViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.TestViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.DiffNs#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.DiffNs#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.DiffNs#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.DiffNs#Views.ProductView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.DiffNs#Views.ProductView.Ivf.verified.cs deleted file mode 100644 index 86a15d83..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.DiffNs#Views.ProductView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: Views.ProductView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace Views -{ - /// - /// Partial class for the ProductView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class ProductView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(ViewModels.ProductViewModel), typeof(ProductView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public ViewModels.ProductViewModel BindingRoot => ViewModel; - - /// - public ViewModels.ProductViewModel ViewModel { get => (ViewModels.ProductViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (ViewModels.ProductViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.ExtNs#App.Views.ExternalView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.ExtNs#App.Views.ExternalView.Ivf.verified.cs deleted file mode 100644 index 3918ed3c..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.ExtNs#App.Views.ExternalView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: App.Views.ExternalView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace App.Views -{ - /// - /// Partial class for the ExternalView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class ExternalView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(External.ViewModels.ExternalViewModel), typeof(ExternalView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public External.ViewModels.ExternalViewModel BindingRoot => ViewModel; - - /// - public External.ViewModels.ExternalViewModel ViewModel { get => (External.ViewModels.ExternalViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (External.ViewModels.ExternalViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.ExtNs#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.ExtNs#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.ExtNs#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWNestViewClass#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWNestViewClass#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWNestViewClass#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWNestViewClass#TestNs.OuterContainer+MiddleContainer+NestView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWNestViewClass#TestNs.OuterContainer+MiddleContainer+NestView.Ivf.verified.cs deleted file mode 100644 index 2cc7e844..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWNestViewClass#TestNs.OuterContainer+MiddleContainer+NestView.Ivf.verified.cs +++ /dev/null @@ -1,41 +0,0 @@ -//HintName: TestNs.OuterContainer+MiddleContainer+NestedView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - public partial class OuterContainer - { - public partial class MiddleContainer - { - /// - /// Partial class for the NestedView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class NestedView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.TestViewModel), typeof(NestedView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.TestViewModel BindingRoot => ViewModel; - - /// - public TestNs.TestViewModel ViewModel { get => (TestNs.TestViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.TestViewModel)value; } - } - } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxCmdsVm#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxCmdsVm#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxCmdsVm#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxCmdsVm#TestNs.CmdsView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxCmdsVm#TestNs.CmdsView.Ivf.verified.cs deleted file mode 100644 index 821ddcb0..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxCmdsVm#TestNs.CmdsView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.CommandsView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the CommandsView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class CommandsView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.CommandsViewModel), typeof(CommandsView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.CommandsViewModel BindingRoot => ViewModel; - - /// - public TestNs.CommandsViewModel ViewModel { get => (TestNs.CommandsViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.CommandsViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxPropsVm#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxPropsVm#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxPropsVm#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxPropsVm#TestNs.RxPropsView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxPropsVm#TestNs.RxPropsView.Ivf.verified.cs deleted file mode 100644 index 50a6e70f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWRxPropsVm#TestNs.RxPropsView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.ReactivePropertiesView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the ReactivePropertiesView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class ReactivePropertiesView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.ReactivePropertiesViewModel), typeof(ReactivePropertiesView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.ReactivePropertiesViewModel BindingRoot => ViewModel; - - /// - public TestNs.ReactivePropertiesViewModel ViewModel { get => (TestNs.ReactivePropertiesViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.ReactivePropertiesViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWStringVmT#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWStringVmT#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWStringVmT#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWStringVmT#TestNs.TestView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWStringVmT#TestNs.TestView.Ivf.verified.cs deleted file mode 100644 index 1db5c03c..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FIvfWStringVmT#TestNs.TestView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.TestView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the TestView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class TestView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.TestViewModel), typeof(TestView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.TestViewModel BindingRoot => ViewModel; - - /// - public TestNs.TestViewModel ViewModel { get => (TestNs.TestViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.TestViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View1.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View1.Ivf.verified.cs deleted file mode 100644 index 80d4f71d..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View1.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.View1.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the View1 which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class View1 : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.ViewModel1), typeof(View1), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.ViewModel1 BindingRoot => ViewModel; - - /// - public TestNs.ViewModel1 ViewModel { get => (TestNs.ViewModel1)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.ViewModel1)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View2.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View2.Ivf.verified.cs deleted file mode 100644 index 77a992c0..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View2.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.View2.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the View2 which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class View2 : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.ViewModel2), typeof(View2), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.ViewModel2 BindingRoot => ViewModel; - - /// - public TestNs.ViewModel2 ViewModel { get => (TestNs.ViewModel2)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.ViewModel2)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View3.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View3.Ivf.verified.cs deleted file mode 100644 index b03bd0a7..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.FMultiIvfInSameNs#TestNs.View3.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.View3.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the View3 which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class View3 : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.ViewModel3), typeof(View3), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.ViewModel3 BindingRoot => ViewModel; - - /// - public TestNs.ViewModel3 ViewModel { get => (TestNs.ViewModel3)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.ViewModel3)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Gen#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Gen#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Gen#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Gen#TestNs.StringItemView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Gen#TestNs.StringItemView.Ivf.verified.cs deleted file mode 100644 index 932a5d20..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Gen#TestNs.StringItemView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.StringItemView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the StringItemView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class StringItemView : global::ReactiveUI.IViewFor> - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.GenericViewModel), typeof(StringItemView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.GenericViewModel BindingRoot => ViewModel; - - /// - public TestNs.GenericViewModel ViewModel { get => (TestNs.GenericViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.GenericViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Interface#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Interface#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Interface#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Interface#TestNs.InterfaceView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Interface#TestNs.InterfaceView.Ivf.verified.cs deleted file mode 100644 index fd0699be..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Interface#TestNs.InterfaceView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.InterfaceView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the InterfaceView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class InterfaceView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.ITestViewModel), typeof(InterfaceView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.ITestViewModel BindingRoot => ViewModel; - - /// - public TestNs.ITestViewModel ViewModel { get => (TestNs.ITestViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.ITestViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Nest#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Nest#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Nest#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Nest#TestNs.ChildView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Nest#TestNs.ChildView.Ivf.verified.cs deleted file mode 100644 index 0c921f68..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Nest#TestNs.ChildView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.ChildView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the ChildView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class ChildView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.ParentViewModel.ChildViewModel), typeof(ChildView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.ParentViewModel.ChildViewModel BindingRoot => ViewModel; - - /// - public TestNs.ParentViewModel.ChildViewModel ViewModel { get => (TestNs.ParentViewModel.ChildViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.ParentViewModel.ChildViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Record#IvfAttr.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Record#IvfAttr.verified.cs deleted file mode 100644 index 038e227f..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Record#IvfAttr.verified.cs +++ /dev/null @@ -1,34 +0,0 @@ -//HintName: ReactiveUI.SourceGenerators.IViewForAttribute.g.cs -// Copyright (c) 2026 .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Record#TestNs.RecordView.Ivf.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Record#TestNs.RecordView.Ivf.verified.cs deleted file mode 100644 index 8698c49e..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/VFE.Record#TestNs.RecordView.Ivf.verified.cs +++ /dev/null @@ -1,35 +0,0 @@ -//HintName: TestNs.RecordView.IViewFor.g.cs -// -using ReactiveUI; -using System.Windows; - -#pragma warning disable -#nullable enable - -namespace TestNs -{ - /// - /// Partial class for the RecordView which contains ReactiveUI IViewFor initialization. - /// - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - public partial class RecordView : global::ReactiveUI.IViewFor - { - /// - /// The view model dependency property. - /// - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TestNs.RecordViewModel), typeof(RecordView), new PropertyMetadata(null)); - - /// - /// Gets the binding root view model. - /// - public TestNs.RecordViewModel BindingRoot => ViewModel; - - /// - public TestNs.RecordViewModel ViewModel { get => (TestNs.RecordViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - - /// - object? global::ReactiveUI.IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestNs.RecordViewModel)value; } - } -} -#nullable restore -#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj b/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj index 12ad884e..8ddb77de 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj +++ b/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj @@ -60,7 +60,6 @@ - diff --git a/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs b/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs index 98872f35..1a9c7833 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs @@ -125,7 +125,6 @@ private static string GetVerifiedFilePath() { nameof(ReactiveGenerator) => "REACTIVE", nameof(ReactiveCommandGenerator) => "REACTIVECMD", - nameof(IViewForGenerator) => "IVIEWFOR", nameof(RoutedControlHostGenerator) => "ROUTEDHOST", nameof(ViewModelControlHostGenerator) => "CONTROLHOST", nameof(BindableDerivedListGenerator) => "DERIVEDLIST", @@ -162,12 +161,6 @@ private static void AddGeneratorSpecificSyntaxTrees(List syntaxTrees AddSyntaxTree(syntaxTrees, GetAttributeDefinitionsMethodResult("GetAccessModifierEnum"), parseOptions, "AccessModifierEnum.g.cs"); } - if (typeof(T) == typeof(IViewForGenerator)) - { - AddSyntaxTree(syntaxTrees, GetAttributeDefinitionsPropertyResult(ReactiveAttributeName), parseOptions, ReactiveAttributeHintName); - AddSyntaxTree(syntaxTrees, GetAttributeDefinitionsPropertyResult("ReactiveCommandAttribute"), parseOptions, "ReactiveCommandAttribute.g.cs"); - } - if (typeof(T) == typeof(ReactiveObjectGenerator)) { AddSyntaxTree(syntaxTrees, GetAttributeDefinitionsPropertyResult(ReactiveAttributeName), parseOptions, ReactiveAttributeHintName); @@ -250,21 +243,13 @@ private static List GetGeneratedSupportSources() private static void AddRequiredAttributeDefinitions(List supportSources) { // Yield each attribute definition only if generator T does NOT inject it. - // Note: for IViewForGenerator, ReactiveAttribute and ReactiveCommandAttribute are - // added as inline SyntaxTrees below (not in the support DLL) so they are accessible - // in the test source compilation without CS0122 internal-visibility errors. - if (typeof(T) != typeof(ReactiveCommandGenerator) && typeof(T) != typeof(IViewForGenerator)) + if (typeof(T) != typeof(ReactiveCommandGenerator)) { supportSources.Add(GetAttributeDefinitionsPropertyResult("ReactiveCommandAttribute")); } AddReactiveAttributeDefinitionIfNeeded(supportSources); - if (typeof(T) != typeof(IViewForGenerator)) - { - supportSources.Add(GetAttributeDefinitionsPropertyResult("IViewForAttribute")); - } - AddRemainingAttributeDefinitions(supportSources); } @@ -272,7 +257,7 @@ private static void AddRequiredAttributeDefinitions(List supportSources) /// The support-source collection to extend. private static void AddReactiveAttributeDefinitionIfNeeded(List supportSources) { - if (typeof(T) == typeof(ReactiveGenerator) || typeof(T) == typeof(IViewForGenerator) || typeof(T) == typeof(ReactiveObjectGenerator) + if (typeof(T) == typeof(ReactiveGenerator) || typeof(T) == typeof(ReactiveObjectGenerator) || typeof(T) == typeof(BindableDerivedListGenerator) || typeof(T) == typeof(ReactiveCollectionGenerator)) { return; diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/AttributeDataExtensionTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/AttributeDataExtensionTests.cs index 517ccf49..643f5f45 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/AttributeDataExtensionTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/AttributeDataExtensionTests.cs @@ -7,7 +7,7 @@ namespace ReactiveUI.SourceGenerator.Tests; -/// Unit tests for covering TryGetNamedArgument, GetNamedArgument, GetConstructorArguments, and GetGenericType. +/// Unit tests for covering TryGetNamedArgument, GetNamedArgument, and GetConstructorArguments. public sealed class AttributeDataExtensionTests { /// TryGetNamedArgument returns true and the correct value when the argument exists. @@ -301,66 +301,6 @@ public class C { } await Assert.That(arguments).IsEquivalentTo(["first", null, "last"]); } - /// GetGenericType returns the type argument name for a generic attribute. - /// A task to monitor the async. - [Test] - public async Task WhenGenericAttributeThenGetGenericTypeReturnsTypeName() - { - const string source = """ - using System; - namespace T; - [AttributeUsage(AttributeTargets.Class)] - public class AttributeTen : Attribute { } - [AttributeTen] - public class C { } - """; - var attribute = GetAttribute(source, "T.C", "AttributeTen"); - - var type = attribute.GetGenericType(); - - await Assert.That(type).IsEqualTo("int"); - } - - /// GetGenericType returns null for a non-generic attribute. - /// A task to monitor the async. - [Test] - public async Task WhenNonGenericAttributeThenGetGenericTypeReturnsNull() - { - const string source = """ - using System; - namespace T; - [AttributeUsage(AttributeTargets.Class)] - public class AttributeEleven : Attribute { } - [AttributeEleven] - public class C { } - """; - var attribute = GetAttribute(source, "T.C", "AttributeEleven"); - - var type = attribute.GetGenericType(); - - await Assert.That(type).IsNull(); - } - - /// GetGenericType returns the type keyword for a generic argument using a built-in type. - /// A task to monitor the async. - [Test] - public async Task WhenGenericAttributeWithClassTypeThenGetGenericTypeReturnsClassName() - { - const string source = """ - using System; - namespace T; - [AttributeUsage(AttributeTargets.Class)] - public class WrapAttr : Attribute { } - [WrapAttr] - public class C { } - """; - var attribute = GetAttribute(source, "T.C", "WrapAttr"); - - var type = attribute.GetGenericType(); - - await Assert.That(type).IsEqualTo("string"); - } - /// GatherForwardedAttributesFromClass collects non-trigger attributes from the class declaration. /// A task to monitor the async. [Test] diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/IViewForGeneratorTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/IViewForGeneratorTests.cs deleted file mode 100644 index fabfd560..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/IViewForGeneratorTests.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved. -// ReactiveUI Association Incorporated licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Text; -using Microsoft.CodeAnalysis.Text; - -namespace ReactiveUI.SourceGenerator.Tests; - -/// Tests view-for source generation. -public class IViewForGeneratorTests : TestBase -{ - /// The generated source count for four supported platform targets: the attribute and one file per view. - private const int SupportedPlatformGeneratedSourceCount = 5; - - /// The generated source count when no target can produce a view implementation: the attribute alone. - private const int NoViewImplementationGeneratedSourceCount = 1; - - /// Tests that the source generator correctly generates reactive properties. - /// A task to monitor the async. - [Test] - public Task Basic() - { - // Arrange: Setup the source code that matches the generator input expectations. - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - [IViewFor] - public partial class TestViewWpf : Window - { - /// - /// Initializes a new instance of the class. - /// - public TestViewWpf() => ViewModel = TestViewModel.Instance; - } - - public partial class TestViewModel : ReactiveObject - { - /// - /// Gets the instance of the test view model. - /// - public static TestViewModel Instance { get; } = new(); - - /// - /// Gets or sets the test property. - /// - public int TestProperty { get; set; } - } - """; - - // Act: Initialize the helper and run the generator. Assert: Verify the generated code. - return TestHelper.TestPass(sourceCode); - } - - /// Verifies the platform-specific IViewFor source shapes without creating snapshots. - /// A task representing the asynchronous assertion work. - [Test] - public async Task GeneratesPlatformSpecificSourcesForSupportedViewBases() - { - var generatedSources = RunIViewForGenerator( - """ - using ReactiveUI.SourceGenerators; - - namespace System.Windows { public class Window { } } - namespace System.Windows.Forms { public class UserControl { } } - namespace Avalonia.Controls { public class UserControl { } } - namespace Microsoft.Maui.Controls { public class ContentPage { } } - - namespace TestNs - { - public sealed class WpfViewModel { } - public sealed class WinFormsViewModel { } - public sealed class AvaloniaViewModel { } - public sealed class MauiViewModel { } - - [IViewFor] - public partial class WpfView : System.Windows.Window { } - - [IViewFor] - public partial class WinFormsView : System.Windows.Forms.UserControl { } - - [IViewFor] - public partial class AvaloniaView : Avalonia.Controls.UserControl { } - - [IViewFor] - public partial class MauiView : Microsoft.Maui.Controls.ContentPage { } - } - """); - - var wpfSource = GetGeneratedSource(generatedSources, "TestNs.WpfView.IViewFor.g.cs"); - var winFormsSource = GetGeneratedSource(generatedSources, "TestNs.WinFormsView.IViewFor.g.cs"); - var avaloniaSource = GetGeneratedSource(generatedSources, "TestNs.AvaloniaView.IViewFor.g.cs"); - var mauiSource = GetGeneratedSource(generatedSources, "TestNs.MauiView.IViewFor.g.cs"); - - await Assert.That(generatedSources.Count).IsEqualTo(SupportedPlatformGeneratedSourceCount); - await Assert.That(wpfSource.Contains("using System.Windows;", StringComparison.Ordinal)).IsTrue(); - await Assert.That(winFormsSource.Contains( - "[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]", - StringComparison.Ordinal)).IsTrue(); - await Assert.That(avaloniaSource.Contains( - "AvaloniaProperty.Register", - StringComparison.Ordinal)).IsTrue(); - await Assert.That(mauiSource.Contains( - "BindableProperty.Create(nameof(ViewModel), typeof(TestNs.MauiViewModel)", - StringComparison.Ordinal)).IsTrue(); - } - - /// Verifies WinUI, Uno and string view models, and that no view registration is generated. - /// A task representing the asynchronous assertion work. - [Test] - public async Task GeneratesWinUiAndUnoViewsWithoutRegistration() - { - var generatedSources = RunIViewForGenerator( - """ - using ReactiveUI.SourceGenerators; - - namespace Microsoft.UI.Xaml.Controls { public class Page { } } - namespace Windows.UI.Xaml.Controls { public class Page { } } - - namespace TestNs - { - public sealed class ViewModel { } - - [IViewFor] - public partial class WinUiView : Microsoft.UI.Xaml.Controls.Page { } - - [IViewFor("TestNs.ViewModel")] - public partial class UnoView : Windows.UI.Xaml.Controls.Page { } - - [IViewFor("")] - public partial class MissingViewModelView : Microsoft.UI.Xaml.Controls.Page { } - } - """); - var winUiSource = GetGeneratedSource(generatedSources, "TestNs.WinUiView.IViewFor.g.cs"); - var unoSource = GetGeneratedSource(generatedSources, "TestNs.UnoView.IViewFor.g.cs"); - - await Assert.That(winUiSource.Contains("using Microsoft.UI.Xaml;", StringComparison.Ordinal)).IsTrue(); - await Assert.That(unoSource.Contains("using Windows.UI.Xaml;", StringComparison.Ordinal)).IsTrue(); - await Assert.That(generatedSources.Any(static source => source.HintName.Contains("MissingViewModelView", StringComparison.Ordinal))).IsFalse(); - await Assert.That(generatedSources.Any(static source => source.SourceText.ToString().Contains("RegisterViewsForViewModelsSourceGenerated", StringComparison.Ordinal))).IsFalse(); - } - - /// Verifies unsupported and non-partial IViewFor targets do not emit view implementations. - /// A task representing the asynchronous assertion work. - [Test] - public async Task DoesNotGenerateViewSourceForUnsupportedOrNonPartialTargets() - { - var generatedSources = RunIViewForGenerator( - """ - using ReactiveUI.SourceGenerators; - - namespace TestNs - { - public sealed class ViewModel { } - - [IViewFor] - public partial class UnsupportedView { } - - [IViewFor] - public class NonPartialView { } - } - """); - - await Assert.That(generatedSources.Count).IsEqualTo(NoViewImplementationGeneratedSourceCount); - await Assert.That(generatedSources.Any(static source => source.HintName.EndsWith(".IViewFor.g.cs", StringComparison.Ordinal))).IsFalse(); - } - - /// Runs the IViewFor generator and returns its emitted sources for focused assertions. - /// The consumer source text. - /// The generated source results. - private static ImmutableArray RunIViewForGenerator(string source) - { - var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview); - var compilation = CSharpCompilation.Create( - "IViewForConsumer", - [CSharpSyntaxTree.ParseText(SourceText.From(source, Encoding.UTF8), parseOptions)], - TestCompilationReferences.CreatePortableDefault(), - new(OutputKind.DynamicallyLinkedLibrary)); - GeneratorDriver driver = CSharpGeneratorDriver.Create([new IViewForGenerator()]).WithUpdatedParseOptions(parseOptions); - driver = driver.RunGenerators(compilation); - - return driver.GetRunResult().Results.Single().GeneratedSources; - } - - /// Gets one generated source by its stable hint name. - /// The sources emitted by the generator. - /// The source hint name to find. - /// The generated source text. - private static string GetGeneratedSource(ImmutableArray generatedSources, string hintName) - { - foreach (var source in generatedSources) - { - if (source.HintName == hintName) - { - return source.SourceText.ToString(); - } - } - - throw new InvalidOperationException($"The IViewFor generator did not produce '{hintName}'."); - } -} diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/IncrementalCachingTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/IncrementalCachingTests.cs index bf4d22db..4e135507 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/IncrementalCachingTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/IncrementalCachingTests.cs @@ -102,23 +102,6 @@ [IReactiveObject] public partial class Second { } "partial class First", "partial class Third"); - /// An unrelated edit leaves every [IViewFor] type cached; editing one type reruns only that type. - /// A task to monitor the async. - [Test] - public Task ViewForCachesPerType() => AssertCachesPerType( - new IViewForGenerator(), - TrackingNames.ViewForTypes, - """ - using ReactiveUI.SourceGenerators; - namespace TestNs; - public sealed class FirstViewModel { } - public sealed class SecondViewModel { } - [IViewFor] public partial class FirstView : System.Windows.Window { } - [IViewFor] public partial class SecondView : System.Windows.Window { } - """, - "partial class FirstView", - "partial class ThirdView"); - /// An unrelated edit leaves every [RoutedControlHost] host cached. /// A task to monitor the async. [Test] diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/SymbolExtensionTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/SymbolExtensionTests.cs index 3af49984..3ed3e257 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/SymbolExtensionTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/SymbolExtensionTests.cs @@ -379,8 +379,6 @@ public class Unrelated { } await Assert.That(derived.ImplementsFullyQualifiedMetadataName("System.ICloneable")).IsFalse(); await Assert.That(derived.HasOrInheritsFromFullyQualifiedMetadataNameStartingWith(TypeMetadataNamePrefix)).IsTrue(); await Assert.That(derived.HasOrInheritsFromFullyQualifiedMetadataNameStartingWith("Missing.Prefix")).IsFalse(); - await Assert.That(derived.InheritsFromFullyQualifiedMetadataNameStartingWith(TypeMetadataNamePrefix)).IsTrue(); - await Assert.That(unrelated.InheritsFromFullyQualifiedMetadataNameStartingWith(TypeMetadataNamePrefix)).IsFalse(); await Assert.That(derived.HasOrInheritsAttributeWithFullyQualifiedMetadataName(ObsoleteAttributeMetadataName)).IsTrue(); await Assert.That(unrelated.HasOrInheritsAttributeWithFullyQualifiedMetadataName(ObsoleteAttributeMetadataName)).IsFalse(); } diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ViewApiIntegrationTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ViewApiIntegrationTests.cs index 2762e29f..dfe8e0d0 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ViewApiIntegrationTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ViewApiIntegrationTests.cs @@ -12,7 +12,7 @@ namespace ReactiveUI.SourceGenerator.Tests; /// -/// Tests that the view generators name IViewFor and the view locator from the assembly the compilation uses: +/// Tests that the Windows Forms host generators name IViewFor and the view locator from the assembly the compilation uses: /// ReactiveUI itself, ReactiveUI.Binding, or ReactiveUI.Binding.Reactive. /// public sealed class ViewApiIntegrationTests @@ -40,22 +40,6 @@ public static class ReactiveCommand { } } """; - /// A Windows Forms view and a WPF view, each declared only through [IViewFor]. - private const string ViewsSource = """ - using ReactiveUI.SourceGenerators; - - namespace Views - { - public sealed class LoginViewModel { } - - [IViewFor] - public partial class FormsLoginView : System.Windows.Forms.UserControl { } - - [IViewFor("Views.LoginViewModel")] - public partial class WpfLoginView : System.Windows.Controls.UserControl { } - } - """; - /// A Windows Forms routed host and view-model host. private const string HostsSource = """ using ReactiveUI.SourceGenerators.WinForms; @@ -101,26 +85,6 @@ public async Task ViewApiNamesTheInterfaceAndLocatorInFull() await Assert.That(bindingReactive.CurrentViewLocator).IsEqualTo("global::ReactiveUI.Binding.Reactive.ViewLocator.GetCurrent()"); } - /// - /// Verifies [IViewFor] views implement ReactiveUI.Binding's interface and compile with no using for its - /// namespace, in each flavour. - /// - /// The ReactiveUI.Binding flavour's namespace. - /// A task representing the asynchronous assertion work. - [Test] - [Arguments(BindingNamespace)] - [Arguments(BindingReactiveNamespace)] - public async Task ViewsImplementTheBindingInterface(string bindingNamespace) - { - var (compilation, generated) = Run(ViewsSource, ".IViewFor.g.cs", BindingStub(bindingNamespace)); - - await Assert.That(GetErrors(compilation)).IsEmpty(); - await Assert.That(generated.Contains($": global::{bindingNamespace}.IViewFor", StringComparison.Ordinal)).IsTrue(); - await Assert.That(generated.Contains($"object? global::{bindingNamespace}.IViewFor.ViewModel", StringComparison.Ordinal)).IsTrue(); - await Assert.That(ImplementsViewFor(compilation, "Views.FormsLoginView", bindingNamespace)).IsTrue(); - await Assert.That(ImplementsViewFor(compilation, "Views.WpfLoginView", bindingNamespace)).IsTrue(); - } - /// Verifies the Windows Forms hosts resolve views through ReactiveUI.Binding's view locator, in each flavour. /// The ReactiveUI.Binding flavour's namespace. /// A task representing the asynchronous assertion work. @@ -218,40 +182,4 @@ private static CSharpCompilation CreateCompilation(params string[] sources) typeof(BindableAttribute).Assembly), new(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable)); } - - /// Checks that a view implements a flavour's IViewFor<T>. - /// The compilation after generation. - /// The view's metadata name. - /// The flavour's namespace. - /// when the view implements the flavour's interface. - private static bool ImplementsViewFor(Compilation compilation, string viewName, string bindingNamespace) - { - var viewFor = compilation.GetTypeByMetadataName($"{bindingNamespace}.IViewFor`1"); - foreach (var implemented in compilation.GetTypeByMetadataName(viewName)!.AllInterfaces) - { - if (SymbolEqualityComparer.Default.Equals(implemented.OriginalDefinition, viewFor)) - { - return true; - } - } - - return false; - } - - /// Formats all compilation errors for assertion output. - /// The compilation to inspect. - /// The formatted compilation errors. - private static string GetErrors(Compilation compilation) - { - var errors = new List(); - foreach (var diagnostic in compilation.GetDiagnostics()) - { - if (diagnostic.Severity == DiagnosticSeverity.Error) - { - errors.Add(diagnostic.ToString()); - } - } - - return string.Join(Environment.NewLine, errors); - } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ViewForExtTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ViewForExtTests.cs deleted file mode 100644 index 41c8d7fa..00000000 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ViewForExtTests.cs +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved. -// ReactiveUI Association Incorporated licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -namespace ReactiveUI.SourceGenerator.Tests; - -/// Extended unit tests for the IViewFor generator covering edge cases. -public class ViewForExtTests : TestBase -{ - /// Tests IViewFor with nested ViewModel. - /// A task to monitor the async. - [Test] - public Task Nested() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public partial class ParentViewModel : ReactiveObject - { - public string? ParentProperty { get; set; } - - public partial class ChildViewModel : ReactiveObject - { - public string? ChildProperty { get; set; } - } - } - - [IViewFor] - public partial class ChildView : Window - { - public ChildView() => ViewModel = new ParentViewModel.ChildViewModel(); - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with string-based ViewModel type. - /// A task to monitor the async. - [Test] - public Task FromIViewForWithStringViewModelType() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - [IViewFor("TestNs.TestViewModel")] - public partial class TestView : Window - { - public TestView() => ViewModel = new TestViewModel(); - } - - public partial class TestViewModel : ReactiveObject - { - public int TestProperty { get; set; } - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with different namespaces for View and ViewModel. - /// A task to monitor the async. - [Test] - public Task DiffNs() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace ViewModels - { - public partial class ProductViewModel : ReactiveObject - { - public string? ProductName { get; set; } - public decimal Price { get; set; } - } - } - - namespace Views - { - using ViewModels; - - [IViewFor] - public partial class ProductView : Window - { - public ProductView() => ViewModel = new ProductViewModel(); - } - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests multiple IViewFor in same namespace. - /// A task to monitor the async. - [Test] - public Task FromMultipleIViewForInSameNamespace() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public partial class ViewModel1 : ReactiveObject - { - public int Property1 { get; set; } - } - - public partial class ViewModel2 : ReactiveObject - { - public string? Property2 { get; set; } - } - - public partial class ViewModel3 : ReactiveObject - { - public bool Property3 { get; set; } - } - - [IViewFor] - public partial class View1 : Window - { - public View1() => ViewModel = new ViewModel1(); - } - - [IViewFor] - public partial class View2 : Window - { - public View2() => ViewModel = new ViewModel2(); - } - - [IViewFor] - public partial class View3 : Window - { - public View3() => ViewModel = new ViewModel3(); - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with generic ViewModel. - /// A task to monitor the async. - [Test] - public Task Generic() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public partial class GenericViewModel - { - public T? Item { get; set; } - } - - [IViewFor>] - public partial class StringItemView : Window - { - public StringItemView() => ViewModel = new GenericViewModel(); - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with record ViewModel. - /// A task to monitor the async. - [Test] - public Task Record() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public partial record RecordViewModel - { - public string? Name { get; set; } - public int Age { get; set; } - } - - [IViewFor] - public partial class RecordView : Window - { - public RecordView() => ViewModel = new RecordViewModel(); - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with deeply nested View class. - /// A task to monitor the async. - [Test] - public Task FromIViewForWithNestedViewClass() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public partial class TestViewModel : ReactiveObject - { - public string? Name { get; set; } - } - - public partial class OuterContainer - { - public partial class MiddleContainer - { - [IViewFor] - public partial class NestedView : Window - { - public NestedView() => ViewModel = new TestViewModel(); - } - } - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with ViewModel having reactive properties. - /// A task to monitor the async. - [Test] - public Task FromIViewForWithReactivePropertiesViewModel() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public partial class ReactivePropertiesViewModel - { - [Reactive] - private string? _firstName; - - [Reactive] - private string? _lastName; - - [Reactive] - private int _age; - } - - [IViewFor] - public partial class ReactivePropertiesView : Window - { - public ReactivePropertiesView() => ViewModel = new ReactivePropertiesViewModel(); - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with ViewModel having ReactiveCommands. - /// A task to monitor the async. - [Test] - public Task FromIViewForWithReactiveCommandsViewModel() - { - const string sourceCode = """ - using System; - using System.Threading.Tasks; - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public partial class CommandsViewModel : ReactiveObject - { - [ReactiveCommand] - private void DoSomething() { } - - [ReactiveCommand] - private async Task SaveAsync() - { - await Task.Delay(10); - } - } - - [IViewFor] - public partial class CommandsView : Window - { - public CommandsView() => ViewModel = new CommandsViewModel(); - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with interface ViewModel type via string. - /// A task to monitor the async. - [Test] - public Task Interface() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace TestNs; - - public interface ITestViewModel - { - string? Name { get; set; } - } - - public partial class TestViewModelImpl : ReactiveObject, ITestViewModel - { - public string? Name { get; set; } - } - - [IViewFor("TestNs.ITestViewModel")] - public partial class InterfaceView : Window - { - public InterfaceView() => ViewModel = new TestViewModelImpl(); - } - """; - - return TestHelper.TestPass(sourceCode); - } - - /// Tests IViewFor with ViewModel from external namespace reference. - /// A task to monitor the async. - [Test] - public Task ExtNs() - { - const string sourceCode = """ - using System.Collections.ObjectModel; - using System.Windows; - using ReactiveUI; - using ReactiveUI.SourceGenerators; - - namespace External.ViewModels - { - public partial class ExternalViewModel : ReactiveObject - { - public string? ExternalProperty { get; set; } - } - } - - namespace App.Views - { - using External.ViewModels; - - [IViewFor] - public partial class ExternalView : Window - { - public ExternalView() => ViewModel = new ExternalViewModel(); - } - } - """; - - return TestHelper.TestPass(sourceCode); - } -} diff --git a/src/ReactiveUI.SourceGenerators.Execute.Maui/IViewForTest.cs b/src/ReactiveUI.SourceGenerators.Execute.Maui/IViewForTest.cs deleted file mode 100644 index 523fd796..00000000 --- a/src/ReactiveUI.SourceGenerators.Execute.Maui/IViewForTest.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved. -// ReactiveUI Association Incorporated licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using ReactiveUI.SourceGenerators; - -namespace SGReactiveUI.SourceGenerators.Test.Maui; - -/// Provides the MAUI test view used to exercise generated IViewFor members. -/// -[IViewFor] -public partial class IViewForTest : Shell; diff --git a/src/ReactiveUI.SourceGenerators.Execute/Program.cs b/src/ReactiveUI.SourceGenerators.Execute/Program.cs index 3e84e7b4..17caea0c 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/Program.cs +++ b/src/ReactiveUI.SourceGenerators.Execute/Program.cs @@ -16,8 +16,8 @@ public static class Program [System.STAThread] public static void Main() { - // The generated RegisterViewsForViewModelsSourceGenerated() is gone: ReactiveUI.Binding's view locator registers - // every IViewFor at compile time. Without ReactiveUI.Binding, register the views with Splat yourself. + // ReactiveUI.Binding's view locator registers every IViewFor view at compile time. Without it, register the + // views with Splat yourself. AppLocator.CurrentMutable.Register>(static () => new TestViewWinForms()); AppLocator.CurrentMutable.Register>>(static () => new TestViewWpf2()); Application.Run(new TestViewWinForms()); diff --git a/src/ReactiveUI.SourceGenerators.Execute/TestViewWinForms.cs b/src/ReactiveUI.SourceGenerators.Execute/TestViewWinForms.cs index cb749090..926aebf6 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/TestViewWinForms.cs +++ b/src/ReactiveUI.SourceGenerators.Execute/TestViewWinForms.cs @@ -2,14 +2,14 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using ReactiveUI.SourceGenerators; +using System.ComponentModel; +using ReactiveUI; namespace SGReactiveUI.SourceGenerators.Test; /// Provides the Windows Forms test view. /// -[IViewFor] -public partial class TestViewWinForms : Form +public partial class TestViewWinForms : Form, IViewFor { /// Initializes a new instance of the class. public TestViewWinForms() @@ -18,4 +18,12 @@ public TestViewWinForms() ViewModel = TestViewModel.Instance; _ = ViewModel.Activator.Activate(); } + + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public TestViewModel? ViewModel { get; set; } + + /// + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestViewModel?)value; } } diff --git a/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf.cs b/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf.cs index 5ca2e60e..0347099a 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf.cs +++ b/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf.cs @@ -4,14 +4,12 @@ using System.Windows; using ReactiveUI; -using ReactiveUI.SourceGenerators; using Splat; namespace SGReactiveUI.SourceGenerators.Test; /// Provides the WPF test view. -[IViewFor] -public partial class TestViewWpf : Window +public class TestViewWpf : Window, IViewFor { /// Initializes a new instance of the class. public TestViewWpf() @@ -25,4 +23,10 @@ public TestViewWpf() /// The test property. /// public int TestProperty { get; set; } + + /// + public TestViewModel? ViewModel { get; set; } + + /// + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestViewModel?)value; } } diff --git a/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf2.cs b/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf2.cs index b5f992e3..38133723 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf2.cs +++ b/src/ReactiveUI.SourceGenerators.Execute/TestViewWpf2.cs @@ -3,15 +3,20 @@ // See the LICENSE file in the project root for full license information. using System.Windows; -using ReactiveUI.SourceGenerators; +using ReactiveUI; namespace SGReactiveUI.SourceGenerators.Test; /// Provides the generic WPF test view. /// -[IViewFor("SGReactiveUI.SourceGenerators.Test.TestViewModel2")] -public partial class TestViewWpf2 : Window +public class TestViewWpf2 : Window, IViewFor> { /// Initializes a new instance of the class. public TestViewWpf2() => ViewModel = new(); + + /// + public TestViewModel2? ViewModel { get; set; } + + /// + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (TestViewModel2?)value; } } diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs index c94862ff..a7da9516 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs @@ -277,47 +277,6 @@ public ReactiveAttribute(params string[] alsoNotify) """; #endif - public const string IViewForAttributeType = "ReactiveUI.SourceGenerators.IViewForAttribute"; - public const string IViewForGenericAttributeType = "ReactiveUI.SourceGenerators.IViewForAttribute`1"; - - public static string IViewForAttribute { get; } = $$""" -// Copyright (c) {{DateTime.Now.Year}} .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. - -// -#pragma warning disable -#nullable enable -namespace ReactiveUI.SourceGenerators; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model. -[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "{{ReactiveGenerator.GeneratorVersion}}")] -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute : global::System.Attribute; - -/// -/// IViewForAttribute. -/// -/// -/// -/// Initializes a new instance of the class. -/// -/// Type of the view model, ensure to use the full type name including namespace. -[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "{{ReactiveGenerator.GeneratorVersion}}")] -[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] -internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute; -#nullable restore -#pragma warning restore -"""; - public const string ViewModelControlHostAttributeType = "ReactiveUI.SourceGenerators.WinForms.ViewModelControlHostAttribute"; public static string ViewModelControlHostAttribute { get; } = $$""" diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/AttributeDataExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/AttributeDataExtensions.cs index bda8a023..59e09cc9 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/AttributeDataExtensions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/AttributeDataExtensions.cs @@ -169,24 +169,6 @@ static void GatherForwardedAttributes( classAttributesInfo = classAttributesInfoBuilder.ToImmutable(); } - - /// Gets the generic type from this instance. - /// The generic type name, if present. - internal string? GetGenericType() - { - var attributeClassName = attributeData.AttributeClass?.ToDisplayString(); - if (string.IsNullOrWhiteSpace(attributeClassName)) - { - return null; - } - - var start = attributeClassName!.IndexOf('<'); - var end = attributeClassName.LastIndexOf('>'); - - return start >= 0 && end > start - ? attributeClassName.Substring(start + 1, end - start - 1) - : null; - } } /// Tries to convert a typed constant to the requested value type. diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs index 9b87b63f..ec1544d1 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs @@ -90,26 +90,6 @@ internal bool HasOrInheritsFromFullyQualifiedMetadataNameStartingWith(string nam return false; } - /// Checks whether or not a given inherits from a specified type. - /// The full name of the type to check for inheritance. - /// Whether the type symbol inherits from . - internal bool InheritsFromFullyQualifiedMetadataNameStartingWith(string name) - { - var baseType = typeSymbol.BaseType; - - while (baseType is not null) - { - if (baseType.ContainsFullyQualifiedMetadataName(name)) - { - return true; - } - - baseType = baseType.BaseType; - } - - return false; - } - /// Checks whether or not a given has or inherits a specified attribute. /// The name of the attribute to look for. /// Whether the type symbol has an attribute with the specified type name. diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TrackingNames.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TrackingNames.cs index 1845933f..1770a760 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TrackingNames.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TrackingNames.cs @@ -37,9 +37,6 @@ internal static class TrackingNames /// The [ReactiveCollection] models grouped per type. internal const string ReactiveCollectionTypes = nameof(ReactiveCollectionTypes); - /// The [IViewFor] view models grouped per type. - internal const string ViewForTypes = nameof(ViewForTypes); - /// The [IReactiveObject] models grouped per type. internal const string ReactiveObjectTypes = nameof(ReactiveObjectTypes); diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.Execute.cs deleted file mode 100644 index 400a4a46..00000000 --- a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.Execute.cs +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved. -// ReactiveUI Association Incorporated licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System.Threading; -using Microsoft.CodeAnalysis; -using ReactiveUI.SourceGenerators.CodeGeneration; -using ReactiveUI.SourceGenerators.Extensions; -using ReactiveUI.SourceGenerators.Helpers; -using ReactiveUI.SourceGenerators.Models; - -namespace ReactiveUI.SourceGenerators; - -/// Contains implementation details for the source generator. -/// -public partial class IViewForGenerator -{ - /// Gets the assembly version used in generated-code metadata. - internal static readonly string GeneratorVersion = typeof(IViewForGenerator).Assembly.GetName().Version.ToString(); - - /// Gets the fully qualified name used in generated-code metadata. - internal static readonly string GeneratorName = typeof(IViewForGenerator).FullName!; - - /// The ReactiveUI namespace every view file imports; IViewFor itself is named in full. - private const string ReactiveUINamespace = "ReactiveUI"; - - /// The modifier that opens each generated public member. - private const string PublicModifier = "public "; - - /// The GeneratedCode attribute stamped on the generated view model property, built once. - private static readonly string GeneratedCodeAttribute = SourceWriterExtensions.GeneratedCodeAttribute(GeneratorName, GeneratorVersion); - - /// Creates the generation model for an annotated class declaration. - /// The Roslyn context for the annotated declaration. - /// The cancellation token. - /// The generation model, or when the target is unsupported. - private static IViewForInfo? GetClassInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token) - { - var symbol = context.TargetSymbol; - token.ThrowIfCancellationRequested(); - - var attributeData = context.Attributes[0]; - - token.ThrowIfCancellationRequested(); - if (symbol is not INamedTypeSymbol classSymbol) - { - return default; - } - - token.ThrowIfCancellationRequested(); - - using var constructorArguments = attributeData.GetConstructorArguments().GetEnumerator(); - var constructorArgument = constructorArguments.MoveNext() ? constructorArguments.Current : null; - - var genericArgument = attributeData.GetGenericType(); - token.ThrowIfCancellationRequested(); - var viewModelTypeName = string.IsNullOrWhiteSpace(constructorArgument) ? genericArgument : constructorArgument; - if (string.IsNullOrWhiteSpace(viewModelTypeName)) - { - return default; - } - - token.ThrowIfCancellationRequested(); - - var viewForBaseType = GetBaseType(classSymbol); - - // Get the containing type info - var targetInfo = TargetInfo.From(classSymbol); - - token.ThrowIfCancellationRequested(); - - return new( - targetInfo, - viewModelTypeName!, - viewForBaseType); - } - - /// Identifies the supported UI framework represented by a class symbol. - /// The class symbol to inspect. - /// The matching supported base type, or . - private static IViewForBaseType GetBaseType(INamedTypeSymbol classSymbol) - { - if (classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("System.Windows.Forms")) - { - return IViewForBaseType.WinForms; - } - - if (classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("System.Windows") - || classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("System.Windows.Controls")) - { - return IViewForBaseType.Wpf; - } - - if (classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("Microsoft.UI.Xaml") - || classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("Microsoft.UI.Xaml.Controls")) - { - return IViewForBaseType.WinUI; - } - - if (classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("Microsoft.Maui")) - { - return IViewForBaseType.Maui; - } - - if (classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("Avalonia")) - { - return IViewForBaseType.Avalonia; - } - - return classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("Windows.UI.Xaml") - || classSymbol.InheritsFromFullyQualifiedMetadataNameStartingWith("Windows.UI.Xaml.Controls") - ? IViewForBaseType.Uno - : IViewForBaseType.None; - } - - /// Generates the partial type source for a supported IViewFor target. - /// The generation model. - /// The qualified namespace declaring IViewFor. - /// The generated source, or for unsupported types. - private static string? GenerateSource(IViewForInfo info, string viewNamespace) - { - var writer = info.BaseType switch - { - IViewForBaseType.Wpf => BeginDependencyPropertyFile("System.Windows"), - IViewForBaseType.WinUI => BeginDependencyPropertyFile("Microsoft.UI.Xaml"), - IViewForBaseType.Uno => BeginDependencyPropertyFile("Windows.UI.Xaml"), - IViewForBaseType.WinForms => BeginFile().Using(ReactiveUINamespace).Using("System.ComponentModel"), - IViewForBaseType.Avalonia => BeginFile().Using("System").Using(ReactiveUINamespace).Using("Avalonia").Using("Avalonia.Controls"), - IViewForBaseType.Maui => BeginFile().Using("System").Using(ReactiveUINamespace).Using("Microsoft.Maui.Controls"), - _ => null, - }; - - if (writer is null) - { - return null; - } - - // Only the dependency-property file opens with the usual pragma pair; the others restore nullability first. - if (info.BaseType is not (IViewForBaseType.Wpf or IViewForBaseType.WinUI or IViewForBaseType.Uno)) - { - _ = writer.Line("#nullable restore").Line("#pragma warning disable").BlankLine(); - } - - var target = info.TargetInfo; - var depth = writer.OpenNamespace(target.TargetNamespace) + writer.OpenContainingTypes(target.ParentInfo); - WriteTypeDeclaration(writer, info, viewNamespace, info.BaseType != IViewForBaseType.Maui); - if (info.BaseType == IViewForBaseType.WinForms) - { - WriteWinFormsMembers(writer, info.ViewModelTypeName, viewNamespace); - } - else if (info.BaseType == IViewForBaseType.Avalonia) - { - WriteAvaloniaMembers(writer, info, viewNamespace); - } - else if (info.BaseType == IViewForBaseType.Maui) - { - WriteMauiMembers(writer, info.ViewModelTypeName, viewNamespace); - } - else - { - WriteDependencyPropertyMembers(writer, info, viewNamespace); - } - - return writer.CloseBlock() - .CloseBlocks(depth) - .RestoreNullableAndWarnings() - .ToStringAndReturn(); - } - - /// Rents a writer and writes the auto-generated marker every view file starts with. - /// The writer. - private static SourceWriter BeginFile() => SourceWriter.Rent().AutoGenerated(); - - /// Starts a WPF, WinUI or Uno view file, whose imports differ only in the XAML namespace. - /// The namespace declaring DependencyProperty. - /// The writer, after the file's header. - private static SourceWriter BeginDependencyPropertyFile(string xamlNamespace) => - BeginFile() - .Using(ReactiveUINamespace) - .Using(xamlNamespace) - .BlankLine() - .DisableWarningsEnableNullable() - .BlankLine(); - - /// Opens the view's partial declaration implementing IViewFor. - /// The writer, at the level of the view's declaration. - /// The generation model. - /// The qualified namespace declaring IViewFor. - /// Whether the declaration carries a summary doc comment. - private static void WriteTypeDeclaration(SourceWriter writer, IViewForInfo info, string viewNamespace, bool includeSummary) - { - var target = info.TargetInfo; - if (includeSummary) - { - _ = writer.Line("/// ") - .Append("/// Partial class for the ").Append(target.TargetName).Line(" which contains ReactiveUI IViewFor initialization.") - .Line("/// "); - } - - foreach (var attribute in AttributeDefinitions.ExcludeFromCodeCoverage) - { - _ = writer.Line(attribute); - } - - _ = writer.BeginPartialType(target).Append(" : ").Append(viewNamespace).Append(".IViewFor<").Append(info.ViewModelTypeName).Line(">").OpenBlock(); - } - - /// Writes the members of a WPF, WinUI or Uno view, backed by a dependency property. - /// The writer, at the level of the view's members. - /// The generation model. - /// The qualified namespace declaring IViewFor. - private static void WriteDependencyPropertyMembers(SourceWriter writer, IViewForInfo info, string viewNamespace) - { - var viewModelType = info.ViewModelTypeName; - _ = writer.Lines(""" - /// - /// The view model dependency property. - /// - """) - .Line(GeneratedCodeAttribute) - .Append("public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(") - .Append(viewModelType).Append("), typeof(").Append(info.TargetInfo.TargetName).Line("), new PropertyMetadata(null));") - .BlankLine(); - WriteViewModelMembers(writer, viewModelType, string.Empty, viewNamespace); - } - - /// Writes the members of a Windows Forms view, backed by an auto-property. - /// The writer, at the level of the view's members. - /// The view model's type name. - /// The qualified namespace declaring IViewFor. - private static void WriteWinFormsMembers(SourceWriter writer, string viewModelType, string viewNamespace) => - _ = writer.Lines(""" - /// - [Category("ReactiveUI")] - [Description("The ViewModel.")] - [Bindable(true)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - """) - .Line(GeneratedCodeAttribute) - .Append(PublicModifier).Append(viewModelType).Line("? ViewModel {get; set; }") - .BlankLine() - .InheritDoc() - .Append("object? ").Append(viewNamespace).Append(".IViewFor.ViewModel {get => ViewModel; set => ViewModel = (").Append(viewModelType).Line("? )value; }"); - - /// Writes the members of an Avalonia view, backed by a styled property kept in step with the data context. - /// The writer, at the level of the view's members. - /// The generation model. - /// The qualified namespace declaring IViewFor. - private static void WriteAvaloniaMembers(SourceWriter writer, IViewForInfo info, string viewNamespace) - { - var viewModelType = info.ViewModelTypeName; - _ = writer.Lines(""" - /// - /// The view model dependency property. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002", Justification = "Generic avalonia property is expected here.")] - """) - .Append("public static readonly StyledProperty<").Append(viewModelType) - .Append("?> ViewModelProperty = AvaloniaProperty.Register<").Append(info.TargetInfo.TargetName).Append(", ").Append(viewModelType) - .Line(">(nameof(ViewModel));") - .BlankLine(); - WriteViewModelMembers(writer, viewModelType, "?", viewNamespace); - _ = writer.BlankLine() - .Lines(""" - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == DataContextProperty) - { - """) - .Indent().Indent() - .Append("if (ReferenceEquals(change.OldValue, ViewModel) && change.NewValue is null or ").Append(viewModelType).Line(")") - .Outdent().Outdent() - .Lines(""" - { - SetCurrentValue(ViewModelProperty, change.NewValue); - } - } - else if (change.Property == ViewModelProperty) - { - if (ReferenceEquals(change.OldValue, DataContext)) - { - SetCurrentValue(DataContextProperty, change.NewValue); - } - } - } - """); - } - - /// Writes the members of a MAUI view, backed by a bindable property kept in step with the binding context. - /// The writer, at the level of the view's members. - /// The view model's type name. - /// The qualified namespace declaring IViewFor. - private static void WriteMauiMembers(SourceWriter writer, string viewModelType, string viewNamespace) - { - _ = writer.Append("public static readonly BindableProperty ViewModelProperty = BindableProperty.Create(nameof(ViewModel), typeof(") - .Append(viewModelType).Append("), typeof(").Append(viewNamespace).Append(".IViewFor<").Append(viewModelType).Append(">), default(").Append(viewModelType) - .Line("), BindingMode.OneWay, propertyChanged: OnViewModelChanged);") - .BlankLine(); - WriteViewModelMembers(writer, viewModelType, "?", viewNamespace); - _ = writer.BlankLine() - .InheritDoc() - .Line("protected override void OnBindingContextChanged()") - .OpenBlock() - .Line("base.OnBindingContextChanged();") - .Append("ViewModel = BindingContext as ").Append(viewModelType).EndStatement() - .CloseBlock() - .BlankLine() - .Line("private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue;"); - } - - /// Writes the binding root and the typed and untyped ViewModel properties over ViewModelProperty. - /// The writer, at the level of the view's members. - /// The view model's type name. - /// The annotation after the view model type: ?, or empty. - /// The qualified namespace declaring IViewFor. - private static void WriteViewModelMembers(SourceWriter writer, string viewModelType, string nullableSuffix, string viewNamespace) => - _ = writer.Lines(""" - /// - /// Gets the binding root view model. - /// - """) - .Append(PublicModifier).Append(viewModelType).Append(nullableSuffix).Line(" BindingRoot => ViewModel;") - .BlankLine() - .InheritDoc() - .Append(PublicModifier).Append(viewModelType).Append(nullableSuffix).Append(" ViewModel { get => (").Append(viewModelType).Append(nullableSuffix) - .Line(")GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); }") - .BlankLine() - .InheritDoc() - .Append("object? ").Append(viewNamespace).Append(".IViewFor.ViewModel { get => ViewModel; set => ViewModel = (").Append(viewModelType).Append(nullableSuffix).Line(")value; }"); -} diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.cs deleted file mode 100644 index 7e7091db..00000000 --- a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved. -// ReactiveUI Association Incorporated licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Collections.Immutable; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using ReactiveUI.SourceGenerators.CodeGeneration; -using ReactiveUI.SourceGenerators.Extensions; -using ReactiveUI.SourceGenerators.Helpers; -using ReactiveUI.SourceGenerators.Models; - -namespace ReactiveUI.SourceGenerators; - -/// A source generator for generating reactive properties. -[Generator(LanguageNames.CSharp)] -public sealed partial class IViewForGenerator : IIncrementalGenerator -{ - /// - public void Initialize(IncrementalGeneratorInitializationContext context) - { - context.RegisterPostInitializationOutput(static ctx => - ctx.AddSource($"{AttributeDefinitions.IViewForAttributeType}.g.cs", SourceText.From(AttributeDefinitions.IViewForAttribute, SourceWriterExtensions.Utf8WithoutBom))); - - // Gather info for all annotated IViewFor Classes - // The generic IViewFor and the IViewFor(string) forms are different metadata names, so each has its own - // ForAttributeWithMetadataName; both share one predicate and one transform. Only a partial class can take the - // generated members, so anything else is rejected from syntax before any binding. - Func predicate = static (node, _) => - node is ClassDeclarationSyntax { AttributeLists.Count: > 0 } declaration && declaration.Modifiers.Any(SyntaxKind.PartialKeyword); - Func transform = static (context, token) => GetClassInfo(context, token); - var named = context.SyntaxProvider.ForAttributeWithMetadataName(AttributeDefinitions.IViewForAttributeType, predicate, transform).Collect(); - var generic = context.SyntaxProvider.ForAttributeWithMetadataName(AttributeDefinitions.IViewForGenericAttributeType, predicate, transform).Collect(); - var types = named.Combine(generic) - .SelectMany(static (pair, _) => Join(pair.Left, pair.Right)) - .GroupByTarget(static info => info.TargetInfo) - .WithTrackingName(TrackingNames.ViewForTypes); - - // View registration is not generated here; ReactiveUI.Binding's view locator registers views. The interface is - // named in full from the compilation's references, so it binds without a using for ReactiveUI.Binding. - context.RegisterSourceOutput(types.Combine(context.ReactiveUiIntegration()), static (context, input) => - { - var info = input.Left[0]; - - // Only a supported UI framework base type gets a source. - if (GenerateSource(info, input.Right.ViewNamespace) is not { } source) - { - return; - } - - context.AddSource($"{info.TargetInfo.FileHintName}.IViewFor.g.cs", source); - }); - } - - /// Joins the views found through each attribute form, dropping the targets that were declined. - /// The views marked [IViewFor("...")]. - /// The views marked [IViewFor<T>]. - /// The views. - private static ImmutableArray Join(ImmutableArray named, ImmutableArray generic) - { - var builder = ImmutableArray.CreateBuilder(named.Length + generic.Length); - foreach (var info in named) - { - if (info is not null) - { - builder.Add(info); - } - } - - foreach (var info in generic) - { - if (info is not null) - { - builder.Add(info); - } - } - - return builder.ToImmutable(); - } -} diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForBaseType.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForBaseType.cs deleted file mode 100644 index aa4373f7..00000000 --- a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForBaseType.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved. -// ReactiveUI Association Incorporated licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -namespace ReactiveUI.SourceGenerators.Models; - -/// Identifies the UI framework base type implemented by an IViewFor target. -internal enum IViewForBaseType -{ - /// No supported base type was found. - None, - /// The target is a Windows Presentation Foundation control. - Wpf, - /// The target is a WinUI control. - WinUI, - /// The target is a Uno Platform control. - Uno, - /// The target is a Windows Forms control. - WinForms, - /// The target is an Avalonia control. - Avalonia, - /// The target is a .NET MAUI control. - Maui, -} diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForInfo.cs deleted file mode 100644 index 5cf97896..00000000 --- a/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved. -// ReactiveUI Association Incorporated licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -namespace ReactiveUI.SourceGenerators.Models; - -/// Contains the information needed to generate an IViewFor implementation. -/// The annotated target type. -/// The fully qualified view model type name. -/// The supported UI framework base type. -internal sealed record IViewForInfo( - TargetInfo TargetInfo, - string ViewModelTypeName, - IViewForBaseType BaseType); diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems index cbd9e634..e48c9f82 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems +++ b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems @@ -40,10 +40,6 @@ - - - - @@ -66,4 +62,4 @@ - \ No newline at end of file + diff --git a/src/TestApps/TestAvaloniaApplication/Views/MainView.axaml.cs b/src/TestApps/TestAvaloniaApplication/Views/MainView.axaml.cs index 78e6d50d..6ddab814 100644 --- a/src/TestApps/TestAvaloniaApplication/Views/MainView.axaml.cs +++ b/src/TestApps/TestAvaloniaApplication/Views/MainView.axaml.cs @@ -3,13 +3,13 @@ // See the LICENSE file in the project root for full license information. using Avalonia.Controls; +using ReactiveUI; namespace AvaloniaApplication1.Views; -/// The main view, whose IViewFor implementation is source generated. +/// The main view. /// -[ReactiveUI.SourceGenerators.IViewFor] -public partial class MainView : UserControl +public partial class MainView : UserControl, IViewFor { /// Initializes a new instance of the class. public MainView() @@ -17,4 +17,10 @@ public MainView() InitializeComponent(); ViewModel = new(); } + + /// + public ViewModels.MainViewModel? ViewModel { get; set; } + + /// + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (ViewModels.MainViewModel?)value; } } diff --git a/src/TestApps/TestMauiApplication/MainPage.xaml.cs b/src/TestApps/TestMauiApplication/MainPage.xaml.cs index 1484f664..61ded8ad 100644 --- a/src/TestApps/TestMauiApplication/MainPage.xaml.cs +++ b/src/TestApps/TestMauiApplication/MainPage.xaml.cs @@ -2,12 +2,13 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using ReactiveUI; + namespace MauiApp1; -/// The main page, whose IViewFor implementation is source generated. +/// The main page. /// -[ReactiveUI.SourceGenerators.IViewFor] -public partial class MainPage : ContentPage +public partial class MainPage : ContentPage, IViewFor { /// Initializes a new instance of the class. public MainPage() @@ -15,4 +16,10 @@ public MainPage() InitializeComponent(); ViewModel = new(); } + + /// + public MainViewModel? ViewModel { get; set; } + + /// + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (MainViewModel?)value; } } diff --git a/src/TestApps/TestWinFormsApplication/Form1.Designer.cs b/src/TestApps/TestWinFormsApplication/Form1.Designer.cs index 37f52964..78d952f2 100644 --- a/src/TestApps/TestWinFormsApplication/Form1.Designer.cs +++ b/src/TestApps/TestWinFormsApplication/Form1.Designer.cs @@ -1,6 +1,5 @@ namespace WinFormsApp1 { - [ReactiveUI.SourceGenerators.IViewFor] partial class Form1 { /// diff --git a/src/TestApps/TestWinFormsApplication/Form1.cs b/src/TestApps/TestWinFormsApplication/Form1.cs index c123c6c3..3f822eb7 100644 --- a/src/TestApps/TestWinFormsApplication/Form1.cs +++ b/src/TestApps/TestWinFormsApplication/Form1.cs @@ -2,11 +2,14 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; +using ReactiveUI; + namespace WinFormsApp1; /// Represents the primary form for the Windows Forms sample application. /// -public partial class Form1 : Form +public partial class Form1 : Form, IViewFor { /// Initializes a new instance of the class. public Form1() @@ -14,4 +17,12 @@ public Form1() InitializeComponent(); ViewModel = new(); } + + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public MainViewModel? ViewModel { get; set; } + + /// + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (MainViewModel?)value; } } diff --git a/src/TestApps/TestWpfApplication/MainWindow.xaml.cs b/src/TestApps/TestWpfApplication/MainWindow.xaml.cs index e224425c..2a4794a4 100644 --- a/src/TestApps/TestWpfApplication/MainWindow.xaml.cs +++ b/src/TestApps/TestWpfApplication/MainWindow.xaml.cs @@ -3,12 +3,12 @@ // See the LICENSE file in the project root for full license information. using System.Windows; +using ReactiveUI; namespace WpfApp1; /// Provides the interaction logic for MainWindow.xaml. -[ReactiveUI.SourceGenerators.IViewFor] -public partial class MainWindow : Window +public partial class MainWindow : Window, IViewFor { /// Initializes a new instance of the class. public MainWindow() @@ -17,4 +17,10 @@ public MainWindow() ViewModel = new(); DataContext = ViewModel; } + + /// + public MainViewModel? ViewModel { get; set; } + + /// + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (MainViewModel?)value; } } diff --git a/src/benchmarks/README.md b/src/benchmarks/README.md index cadb3cbd..93e26b4d 100644 --- a/src/benchmarks/README.md +++ b/src/benchmarks/README.md @@ -44,7 +44,10 @@ All figures are on .NET 10 (x64, Linux). "Before" is the generators with `[ObservableAsProperty]` and view registration already removed, but still building their output with interpolated strings, binding every attribute for `[IViewFor]` and `[IReactiveObject]`, and writing every file from one collected output. It was measured with this same harness copied onto it. "After" is the generators -as they are now. +once those changes were made, in PR #501. + +`[IViewFor]` has since been removed, and its corpus with it; its row and the whole-corpus and rerun figures below still +include it. ### Generation diff --git a/src/benchmarks/ReactiveUI.SourceGenerators.Benchmarks/Mocks/IViewFor.cs b/src/benchmarks/ReactiveUI.SourceGenerators.Benchmarks/Mocks/IViewFor.cs deleted file mode 100644 index cc67792f..00000000 --- a/src/benchmarks/ReactiveUI.SourceGenerators.Benchmarks/Mocks/IViewFor.cs +++ /dev/null @@ -1,27 +0,0 @@ -using ReactiveUI.Reactive; -using ReactiveUI.SourceGenerators; - -namespace Mocks.IViewFor__N__; - -public sealed class LoginViewModel : ReactiveObject -{ -} - -public sealed class DashboardViewModel : ReactiveObject -{ -} - -[IViewFor] -public partial class LoginView : System.Windows.Controls.UserControl -{ -} - -[IViewFor] -public partial class DashboardView : System.Windows.Forms.UserControl -{ -} - -[IViewFor("Mocks.IViewFor__N__.LoginViewModel")] -public partial class LoginWindow : System.Windows.Window -{ -} diff --git a/src/benchmarks/ReactiveUI.SourceGenerators.Benchmarks/Support/GeneratorCatalog.cs b/src/benchmarks/ReactiveUI.SourceGenerators.Benchmarks/Support/GeneratorCatalog.cs index 8c11ba70..9ee7b5a6 100644 --- a/src/benchmarks/ReactiveUI.SourceGenerators.Benchmarks/Support/GeneratorCatalog.cs +++ b/src/benchmarks/ReactiveUI.SourceGenerators.Benchmarks/Support/GeneratorCatalog.cs @@ -18,7 +18,6 @@ internal static class GeneratorCatalog "ReactiveObject", "BindableDerivedList", "ReactiveCollection", - "IViewFor", "ViewModelControlHost", "RoutedControlHost", GeneratorHarness.AllCorpus, @@ -39,7 +38,6 @@ internal static ISourceGenerator[] Create(string corpus) => "ReactiveObject" => [new ReactiveGenerator().AsSourceGenerator(), new ReactiveObjectGenerator().AsSourceGenerator()], "BindableDerivedList" => [new ReactiveGenerator().AsSourceGenerator(), new BindableDerivedListGenerator().AsSourceGenerator()], "ReactiveCollection" => [new ReactiveGenerator().AsSourceGenerator(), new ReactiveCollectionGenerator().AsSourceGenerator()], - "IViewFor" => [new ReactiveGenerator().AsSourceGenerator(), new IViewForGenerator().AsSourceGenerator()], "ViewModelControlHost" => [new ReactiveGenerator().AsSourceGenerator(), new ViewModelControlHostGenerator().AsSourceGenerator()], "RoutedControlHost" => [new ReactiveGenerator().AsSourceGenerator(), new RoutedControlHostGenerator().AsSourceGenerator()], _ => Create(), @@ -54,7 +52,6 @@ internal static ISourceGenerator[] Create() => new ReactiveObjectGenerator().AsSourceGenerator(), new BindableDerivedListGenerator().AsSourceGenerator(), new ReactiveCollectionGenerator().AsSourceGenerator(), - new IViewForGenerator().AsSourceGenerator(), new ViewModelControlHostGenerator().AsSourceGenerator(), new RoutedControlHostGenerator().AsSourceGenerator(), ];