Skip to content

Commit 462fe4b

Browse files
committed
Add ViewModelRegistrationType to IViewFor attribute
Introduces ViewModelRegistrationType to the IViewFor attribute and updates generator logic to support separate Splat registration for view models. Documentation, tests, and source generator code are updated to reflect the new option and ensure correct registration behavior.
1 parent 33e7e65 commit 462fe4b

File tree

7 files changed

+86
-22
lines changed

7 files changed

+86
-22
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ The class must inherit from a UI Control from any of the following platforms and
560560

561561
### IViewFor with Splat Registration Type
562562

563-
Choose from the following Splat Registration Types:
563+
Choose from the following Splat Registration Types, option for IViewFor Registration and / or ViewModel Registration:
564564
- `SplatRegistrationType.PerRequest`
565565
- `SplatRegistrationType.LazySingleton`
566566
- `SplatRegistrationType.Constant`
@@ -569,7 +569,7 @@ Choose from the following Splat Registration Types:
569569
```csharp
570570
using ReactiveUI.SourceGenerators;
571571
using Splat;
572-
[IViewFor<MyReactiveClass>(RegistrationType = SplatRegistrationType.PerRequest)]
572+
[IViewFor<MyReactiveClass>(RegistrationType = SplatRegistrationType.PerRequest, ViewModelRegistrationType = SplatRegistrationType.LazySingleton)]
573573
public partial class MyReactiveControl : UserControl
574574
{
575575
public MyReactiveControl()

src/ReactiveUI.SourceGenerator.Tests/IVIEWFOR/IViewForGeneratorTests.FromIViewFor#ReactiveUI.SourceGenerators.IViewForAttribute.g.verified.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@ namespace ReactiveUI.SourceGenerators;
2121
internal sealed class IViewForAttribute<T> : global::System.Attribute
2222
{
2323
/// <summary>
24-
/// Gets the Splat registration type for Splat registration.
24+
/// Gets the Splat registration type for Splat IViewFor registration.
2525
/// Registers IViewFor<T> in the Splat service locator.
2626
/// </summary>
2727
public SplatRegistrationType RegistrationType { get; init; } = SplatRegistrationType.None;
28+
29+
/// <summary>
30+
/// Gets the Splat registration type for Splat View Model registration.
31+
/// Registers IViewFor<T> in the Splat service locator.
32+
/// </summary>
33+
public SplatRegistrationType ViewModelRegistrationType { get; init; } = SplatRegistrationType.None;
2834
}
2935

3036
/// <summary>
@@ -39,10 +45,16 @@ internal sealed class IViewForAttribute<T> : global::System.Attribute
3945
internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute
4046
{
4147
/// <summary>
42-
/// Gets the Splat registration type for Splat registration.
48+
/// Gets the Splat registration type for Splat IViewFor registration.
4349
/// Registers IViewFor<T> in the Splat service locator.
4450
/// </summary>
4551
public SplatRegistrationType RegistrationType { get; init; } = SplatRegistrationType.None;
52+
53+
/// <summary>
54+
/// Gets the Splat registration type for Splat View Model registration.
55+
/// Registers IViewFor<T> in the Splat service locator.
56+
/// </summary>
57+
public SplatRegistrationType ViewModelRegistrationType { get; init; } = SplatRegistrationType.None;
4658
}
4759
#nullable restore
4860
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,17 @@ namespace ReactiveUI.SourceGenerator.Tests;
3030
public sealed class TestHelper<T> : IDisposable
3131
where T : IIncrementalGenerator, new()
3232
{
33-
#pragma warning disable CS0618 // Type or member is obsolete
3433
/// <summary>
3534
/// Represents the NuGet library dependency for the Splat library.
3635
/// </summary>
3736
private static readonly LibraryRange SplatLibrary =
38-
new("Splat", VersionRange.AllStableFloating, LibraryDependencyTarget.Package);
37+
new("Splat", VersionRange.AllStable, LibraryDependencyTarget.Package);
3938

4039
/// <summary>
4140
/// Represents the NuGet library dependency for the ReactiveUI library.
4241
/// </summary>
4342
private static readonly LibraryRange ReactiveuiLibrary =
44-
new("ReactiveUI", VersionRange.AllStableFloating, LibraryDependencyTarget.Package);
45-
#pragma warning restore CS0618 // Type or member is obsolete
43+
new("ReactiveUI", VersionRange.AllStable, LibraryDependencyTarget.Package);
4644

4745
private static readonly string mscorlibPath = Path.Combine(
4846
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(),

src/ReactiveUI.SourceGenerators.Execute/TestViewWpf.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace SGReactiveUI.SourceGenerators.Test;
1313
/// <summary>
1414
/// TestView.
1515
/// </summary>
16-
[IViewFor<TestViewModel>(RegistrationType = SplatRegistrationType.PerRequest)]
16+
[IViewFor<TestViewModel>(RegistrationType = SplatRegistrationType.PerRequest, ViewModelRegistrationType = SplatRegistrationType.Constant)]
1717
public partial class TestViewWpf : Window
1818
{
1919
/// <summary>

src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ namespace ReactiveUI.SourceGenerators;
7979
/// ReactiveObjectAttribute.
8080
/// </summary>
8181
/// <seealso cref="System.Attribute" />
82-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveObjectGenerator", "1.1.0.0")]
82+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveObjectGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
8383
[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
8484
internal sealed class ReactiveObjectAttribute : global::System.Attribute;
8585
#nullable restore
@@ -109,7 +109,7 @@ namespace ReactiveUI.SourceGenerators;
109109
/// ReativeCommandAttribute.
110110
/// </summary>
111111
/// <seealso cref="Attribute" />
112-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveCommandGenerator", "1.1.0.0")]
112+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveCommandGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
113113
[global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
114114
internal sealed class ReactiveCommandAttribute : global::System.Attribute
115115
{
@@ -242,7 +242,7 @@ namespace ReactiveUI.SourceGenerators;
242242
/// ObservableAsPropertyAttribute.
243243
/// </summary>
244244
/// <seealso cref="Attribute" />
245-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ObservableAsPropertyGenerator", "1.1.0.0")]
245+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ObservableAsPropertyGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
246246
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property | global::System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
247247
internal sealed class ObservableAsPropertyAttribute : global::System.Attribute
248248
{
@@ -297,7 +297,7 @@ namespace ReactiveUI.SourceGenerators;
297297
/// ObservableAsPropertyAttribute.
298298
/// </summary>
299299
/// <seealso cref="Attribute" />
300-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ObservableAsPropertyGenerator", "1.1.0.0")]
300+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ObservableAsPropertyGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
301301
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property | global::System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
302302
internal sealed class ObservableAsPropertyAttribute : global::System.Attribute
303303
{
@@ -350,15 +350,21 @@ namespace ReactiveUI.SourceGenerators;
350350
/// Initializes a new instance of the <see cref="IViewForAttribute"/> class.
351351
/// </remarks>
352352
/// <param name="viewModelType">Type of the view model.</param>
353-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "1.1.0.0")]
353+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
354354
[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
355355
internal sealed class IViewForAttribute<T> : global::System.Attribute
356356
{
357357
/// <summary>
358-
/// Gets the Splat registration type for Splat registration.
358+
/// Gets the Splat registration type for Splat IViewFor registration.
359359
/// Registers IViewFor<T> in the Splat service locator.
360360
/// </summary>
361361
public SplatRegistrationType RegistrationType { get; init; } = SplatRegistrationType.None;
362+
363+
/// <summary>
364+
/// Gets the Splat registration type for Splat View Model registration.
365+
/// Registers IViewFor<T> in the Splat service locator.
366+
/// </summary>
367+
public SplatRegistrationType ViewModelRegistrationType { get; init; } = SplatRegistrationType.None;
362368
}
363369
364370
/// <summary>
@@ -369,15 +375,21 @@ internal sealed class IViewForAttribute<T> : global::System.Attribute
369375
/// Initializes a new instance of the <see cref="IViewForAttribute"/> class.
370376
/// </remarks>
371377
/// <param name="viewModelType">Type of the view model, ensure to use the full type name including namespace.</param>
372-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "1.1.0.0")]
378+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
373379
[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
374380
internal sealed class IViewForAttribute(string? viewModelType) : global::System.Attribute
375381
{
376382
/// <summary>
377-
/// Gets the Splat registration type for Splat registration.
383+
/// Gets the Splat registration type for Splat IViewFor registration.
378384
/// Registers IViewFor<T> in the Splat service locator.
379385
/// </summary>
380386
public SplatRegistrationType RegistrationType { get; init; } = SplatRegistrationType.None;
387+
388+
/// <summary>
389+
/// Gets the Splat registration type for Splat View Model registration.
390+
/// Registers IViewFor<T> in the Splat service locator.
391+
/// </summary>
392+
public SplatRegistrationType ViewModelRegistrationType { get; init; } = SplatRegistrationType.None;
381393
}
382394
#nullable restore
383395
#pragma warning restore
@@ -404,7 +416,7 @@ namespace ReactiveUI.SourceGenerators.WinForms;
404416
/// Initializes a new instance of the <see cref="ViewModelControlHostAttribute"/> class.
405417
/// </remarks>
406418
/// <param name="viewModelType">Type of the view model.</param>
407-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ViewModelControlHostGenerator", "1.1.0.0")]
419+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ViewModelControlHostGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
408420
[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
409421
internal sealed class ViewModelControlHostAttribute(string? baseType) : global::System.Attribute;
410422
#nullable restore
@@ -432,7 +444,7 @@ namespace ReactiveUI.SourceGenerators.WinForms;
432444
/// Initializes a new instance of the <see cref="RoutedControlHostAttribute"/> class.
433445
/// </remarks>
434446
/// <param name="viewModelType">Type of the view model.</param>
435-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.RoutedControlHostGenerator", "1.1.0.0")]
447+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.RoutedControlHostGenerator", "{{ReactiveGenerator.GeneratorVersion}}")]
436448
[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
437449
internal sealed class RoutedControlHostAttribute(string? baseType) : global::System.Attribute;
438450
#nullable restore

src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.Execute.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,24 @@ public partial class IViewForGenerator
103103
_ => string.Empty,
104104
};
105105

106+
token.ThrowIfCancellationRequested();
107+
108+
// Get RegistrationType enum value from the attribute
109+
attributeData.TryGetNamedArgument("ViewModelRegistrationType", out int splatViewModelRegistrationType);
110+
var viewModelRegistrationType = splatViewModelRegistrationType switch
111+
{
112+
1 => "RegisterLazySingleton",
113+
2 => "RegisterConstant",
114+
3 => "Register",
115+
_ => string.Empty,
116+
};
117+
106118
return new(
107119
targetInfo,
108120
viewModelTypeName!,
109121
viewForBaseType,
110-
registrationType);
122+
registrationType,
123+
viewModelRegistrationType);
111124
}
112125

113126
private static string GenerateSource(string containingTypeName, string containingNamespace, string containingClassVisibility, string containingType, IViewForInfo iviewForInfo)
@@ -322,6 +335,12 @@ private static string GenerateRegistrationExtensions(in ImmutableArray<IViewForI
322335
.Select(static g => g.First())
323336
.ToImmutableArray();
324337

338+
var viewModelRegistrations = iviewForInfo
339+
.Where(static x => !string.IsNullOrWhiteSpace(x.SplatViewModelRegistrationType))
340+
.GroupBy(static x => (x.TargetInfo.TargetNamespaceWithNamespace, x.ViewModelTypeName, x.SplatRegistrationType))
341+
.Select(static g => g.First())
342+
.ToImmutableArray();
343+
325344
var sb = new StringBuilder();
326345
sb.AppendLine("if (resolver is null) throw new global::System.ArgumentNullException(nameof(resolver));");
327346
foreach (var item in registrations)
@@ -350,6 +369,29 @@ private static string GenerateRegistrationExtensions(in ImmutableArray<IViewForI
350369
}
351370
}
352371

372+
foreach (var item in viewModelRegistrations)
373+
{
374+
var vmType = item.ViewModelTypeName;
375+
if (!string.IsNullOrEmpty(vmType) && !vmType.StartsWith("global::", System.StringComparison.Ordinal))
376+
{
377+
vmType = "global::" + vmType;
378+
}
379+
380+
// resolver.Register*/<VM, VM>();
381+
switch (item.SplatViewModelRegistrationType)
382+
{
383+
case "RegisterLazySingleton":
384+
sb.AppendLine($" resolver.{item.SplatViewModelRegistrationType}<{vmType}>(() => new {vmType}());");
385+
break;
386+
case "Register":
387+
sb.AppendLine($" resolver.{item.SplatViewModelRegistrationType}<{vmType}, {vmType}>();");
388+
break;
389+
case "RegisterConstant":
390+
sb.AppendLine($" resolver.{item.SplatViewModelRegistrationType}<{vmType}>(new {vmType}());");
391+
break;
392+
}
393+
}
394+
353395
var registrationsBody = sb.ToString().TrimEnd();
354396
return
355397
$$"""

src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// The ReactiveUI and contributors licenses this file to you under the MIT license.
44
// See the LICENSE file in the project root for full license information.
55

6-
using ReactiveUI.SourceGenerators.Helpers;
76
using ReactiveUI.SourceGenerators.Models;
87

98
namespace ReactiveUI.SourceGenerators.Input.Models;
@@ -15,4 +14,5 @@ internal sealed record IViewForInfo(
1514
TargetInfo TargetInfo,
1615
string ViewModelTypeName,
1716
IViewForBaseType BaseType,
18-
string SplatRegistrationType);
17+
string SplatRegistrationType,
18+
string SplatViewModelRegistrationType);

0 commit comments

Comments
 (0)