Skip to content

Commit 9e86c87

Browse files
authored
Feature Add OutputScheduler to ReactiveCommand (#166)
* Feature Add OutputScheduler to ReactiveCommand * Update Documentation
1 parent 4fc8211 commit 9e86c87

16 files changed

+281
-11
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2023-2025 ReactiveUI
3+
Copyright (c) ReactiveUI 2023-2025
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ ReactiveUI Source Generators automatically generate ReactiveUI objects to stream
1616
- `[ObservableAsProperty(PropertyName = "ReadOnlyPropertyName")]`
1717
- `[ReactiveCommand]`
1818
- `[ReactiveCommand(CanExecute = nameof(IObservableBoolName))]` with CanExecute
19+
- `[ReactiveCommand(OutputScheduler = "RxApp.MainThreadScheduler")]` using a ReactiveUI Scheduler
20+
- `[ReactiveCommand(OutputScheduler = nameof(_isheduler))]` using a Scheduler defined in the class
1921
- `[ReactiveCommand][property: AttributeToAddToCommand]` with Attribute passthrough
2022
- `[IViewFor(nameof(ViewModelName))]`
2123
- `[RoutedControlHost("YourNameSpace.CustomControl")]`
@@ -411,6 +413,30 @@ public partial class MyReactiveClass
411413
}
412414
```
413415

416+
### Usage ReactiveCommand with ReactiveUI OutputScheduler
417+
```csharp
418+
using ReactiveUI.SourceGenerators;
419+
420+
public partial class MyReactiveClass
421+
{
422+
[ReactiveCommand(OutputScheduler = "RxApp.MainThreadScheduler")]
423+
private void Execute() { }
424+
}
425+
```
426+
427+
### Usage ReactiveCommand with custom OutputScheduler
428+
```csharp
429+
using ReactiveUI.SourceGenerators;
430+
431+
public partial class MyReactiveClass
432+
{
433+
private IScheduler _customScheduler = new TestScheduler();
434+
435+
[ReactiveCommand(OutputScheduler = nameof(_customScheduler))]
436+
private void Execute() { }
437+
}
438+
```
439+
414440
## Usage IViewFor `[IViewFor(nameof(ViewModelName))]`
415441

416442
### IViewFor usage

src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveAsyncCommand#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ internal sealed class ReactiveCommandAttribute : Attribute
2525
/// The name of the CanExecute Observable of bool.
2626
/// </value>
2727
public string? CanExecute { get; init; }
28+
29+
/// <summary>
30+
/// Gets the output scheduler.
31+
/// </summary>
32+
/// <value>
33+
/// The output scheduler.
34+
/// </value>
35+
public string? OutputScheduler { get; init; }
2836
}
2937
#nullable restore
3038
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveAsyncCommandWithParameter#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ internal sealed class ReactiveCommandAttribute : Attribute
2525
/// The name of the CanExecute Observable of bool.
2626
/// </value>
2727
public string? CanExecute { get; init; }
28+
29+
/// <summary>
30+
/// Gets the output scheduler.
31+
/// </summary>
32+
/// <value>
33+
/// The output scheduler.
34+
/// </value>
35+
public string? OutputScheduler { get; init; }
2836
}
2937
#nullable restore
3038
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveCommand#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ internal sealed class ReactiveCommandAttribute : Attribute
2525
/// The name of the CanExecute Observable of bool.
2626
/// </value>
2727
public string? CanExecute { get; init; }
28+
29+
/// <summary>
30+
/// Gets the output scheduler.
31+
/// </summary>
32+
/// <value>
33+
/// The output scheduler.
34+
/// </value>
35+
public string? OutputScheduler { get; init; }
2836
}
2937
#nullable restore
3038
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveCommandWithNestedClasses#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ internal sealed class ReactiveCommandAttribute : Attribute
2525
/// The name of the CanExecute Observable of bool.
2626
/// </value>
2727
public string? CanExecute { get; init; }
28+
29+
/// <summary>
30+
/// Gets the output scheduler.
31+
/// </summary>
32+
/// <value>
33+
/// The output scheduler.
34+
/// </value>
35+
public string? OutputScheduler { get; init; }
2836
}
2937
#nullable restore
3038
#pragma warning restore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//HintName: ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.cs
2+
// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved.
3+
// Licensed to the .NET Foundation under one or more agreements.
4+
// The .NET Foundation licenses this file to you under the MIT license.
5+
// See the LICENSE file in the project root for full license information.
6+
7+
using System;
8+
9+
// <auto-generated/>
10+
#pragma warning disable
11+
#nullable enable
12+
namespace ReactiveUI.SourceGenerators;
13+
14+
/// <summary>
15+
/// ReativeCommandAttribute.
16+
/// </summary>
17+
/// <seealso cref="Attribute" />
18+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
19+
internal sealed class ReactiveCommandAttribute : Attribute
20+
{
21+
/// <summary>
22+
/// Gets the can execute method or property.
23+
/// </summary>
24+
/// <value>
25+
/// The name of the CanExecute Observable of bool.
26+
/// </value>
27+
public string? CanExecute { get; init; }
28+
29+
/// <summary>
30+
/// Gets the output scheduler.
31+
/// </summary>
32+
/// <value>
33+
/// The output scheduler.
34+
/// </value>
35+
public string? OutputScheduler { get; init; }
36+
}
37+
#nullable restore
38+
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveCommandWithParameter#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ internal sealed class ReactiveCommandAttribute : Attribute
2525
/// The name of the CanExecute Observable of bool.
2626
/// </value>
2727
public string? CanExecute { get; init; }
28+
29+
/// <summary>
30+
/// Gets the output scheduler.
31+
/// </summary>
32+
/// <value>
33+
/// The output scheduler.
34+
/// </value>
35+
public string? OutputScheduler { get; init; }
2836
}
2937
#nullable restore
3038
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveCMDGeneratorTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,32 @@ private async Task Test3(string baseString)
134134
return TestHelper.TestPass(sourceCode);
135135
}
136136

137+
/// <summary>
138+
/// Froms the reactive command with output scheduler.
139+
/// </summary>
140+
/// <returns>A task to monitor the async.</returns>
141+
[Fact]
142+
public Task FromReactiveCommandWithOutputScheduler()
143+
{
144+
// Arrange: Setup the source code that matches the generator input expectations.
145+
const string sourceCode = """
146+
using System;
147+
using ReactiveUI;
148+
using ReactiveUI.SourceGenerators;
149+
using System.Reactive.Linq;
150+
using System.Threading.Tasks;
151+
namespace TestNs;
152+
public partial class TestVM : ReactiveObject
153+
{
154+
[ReactiveCommand(OutputScheduler = "RxApp.MainThreadScheduler")]
155+
private int Test1() => 10;
156+
}
157+
""";
158+
159+
// Act: Initialize the helper and run the generator. Assert: Verify the generated code.
160+
return TestHelper.TestPass(sourceCode);
161+
}
162+
137163
/// <summary>
138164
/// Froms the reactive command with nested classes.
139165
/// </summary>

src/ReactiveUI.SourceGenerators.Execute/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public static class Program
1313
/// <summary>
1414
/// Defines the entry point of the application.
1515
/// </summary>
16-
public static void Main() => _ = TestViewModel.Instance;
16+
public static void Main() => Application.Run(new TestViewWinForms());
1717
}

0 commit comments

Comments
 (0)