Skip to content

ReactiveUI.SourceGenerators

With ReactiveUI.SourceGenerators.

  • Minimum Requirements:
    • C# Version: 12.0
    • Visual Studio Version: 17.8.0
    • ReactiveUI Version: 19.5.31+

These Source Generators were designed to work in full with ReactiveUI V19.5.31 and newer supporting all features, currently:

  • [Reactive] With field and access modifiers, partial property support (C# 13 Visual Studio Version 17.12.0)
  • [ReactiveCommand]
  • [ReactiveCommand(CanExecute = nameof(IObservableBoolName))] with CanExecute
  • [ReactiveCommand(OutputScheduler = "RxSchedulers.MainThreadScheduler")] using a ReactiveUI Scheduler
  • [ReactiveCommand(OutputScheduler = nameof(_isheduler))] using a Scheduler defined in the class
  • [ReactiveCommand][property: AttributeToAddToCommand] with Attribute passthrough
  • [IViewFor(nameof(ViewModelName))]
  • [RoutedControlHost("YourNameSpace.CustomControl")]
  • [ViewModelControlHost("YourNameSpace.CustomControl")]

Versions older than V19.5.31 to this:

  • All functions fully supported, except for [ReactiveCommand] all supported except Cancellation Token asnyc methods.

The Source Generators are not a direct replacement for ReactiveUI.Fody, but they can be used together. You can continue to use ReactiveUI.Fody and migrate to ReactiveUI.SourceGenerators at your own pace.

As fody operates at the IL level, it can be used to generate properties that directly replace the code you specified in the Property templates for [Reactive] and [ObservableAsProperty] properties. Source Generators add to your code instead of replacing it, so you we use fields and methods to generate the properties and commands.

The [Reactive] Attribute is applied to fields, and the Source Generator will generate the properties for you. [Reactive] will generate a property with a backing field and the RaiseAndSetIfChanged method. You can provide initializers for the field.

The [ReactiveCommand] Attribute is applied to methods, and the Source Generator will generate a ReactiveCommand property for you. The method can be one of the following

  • a void method,
  • a method with a return value,
  • a method with a return value and a parameter,
  • a method with a return value of Task.
  • a method with a return value of Task and a parameter,
  • a method with a return value of Task and a CancellationToken,
  • a method with a return value of Task and a parameter and a CancellationToken,
  • a method with a return value of Task of T.
  • a method with a return value of Task of T and a parameter,
  • a method with a return value of Task of T and a CancellationToken,
  • a method with a return value of Task of T and a parameter and a CancellationToken,
  • a method with a return value of IObservable,
  • a method with a return value of IObservable and a parameter.

Usage Reactive property [Reactive]

Usage Reactive property with field

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass : ReactiveObject
{
    [Reactive]
    private string _myProperty;
}

Usage Reactive property with set Access Modifier

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass : ReactiveObject
{
    [Reactive(SetModifier = AccessModifier.Protected)]
    private string _myProperty;
}

Usage Reactive property with property Attribute pass through

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass : ReactiveObject
{
    [Reactive]
    [property: JsonIgnore]
    private string _myProperty;
}

Usage Reactive property with initial value

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass : ReactiveObject
{
    [Reactive]
    private string _myProperty = "Default Value";
}

Usage Reactive property from partial property

Partial properties are supported in C# 13 and Visual Studio 17.12.0 and later. Both the getter and setter must be empty, and the [Reactive] attribute must be placed on the property. Override and Virtual properties are supported. Set Access Modifier is also supported on partial properties.

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass : ReactiveObject
{
    [Reactive]
    public partial string MyProperty { get; set; }
}

Read-only properties with [ObservableAsProperty]

ReactiveUI.SourceGenerators does not generate [ObservableAsProperty]. ReactiveUI.Binding provides it, and the ReactiveUI package brings ReactiveUI.Binding with it.

Declare the property as partial and get-only, and mark it [ObservableAsProperty]. The generator writes the property body and a field named _{name}Helper. Assign that field in the constructor with ToProperty. Partial properties need C# 13 or later.

using ReactiveUI;
using ReactiveUI.Primitives.Signals;

public partial class MyReactiveClass : ReactiveObject
{
    public MyReactiveClass()
    {
        _myPropertyHelper = MyPropertyObservable()
            .ToProperty(this, static x => x.MyProperty, initialValue: "Default Value");
    }

    [ObservableAsProperty]
    public partial string MyProperty { get; }

    private static IObservable<string> MyPropertyObservable() => Signal.Emit("Test Value");
}

MyProperty returns "Default Value" until the stream produces its first value. Below C# 13, write the ObservableAsPropertyHelper<T> field and the property yourself, as in The manual way to create properties. Properties backed by observables covers both forms. To move code that used the earlier ReactiveUI.SourceGenerators attribute, see the ReactiveUI.Binding migration guide.

Usage ReactiveCommand [ReactiveCommand]

Note: InitializeCommands(); has been removed from the latest version of the Source Generators. This is now handled by the Source Generator.

Usage ReactiveCommand without parameter

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    [ReactiveCommand]
    private void Execute() { }
}

Usage ReactiveCommand with parameter

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    [ReactiveCommand]
    private void Execute(string parameter) { }
}

Usage ReactiveCommand with parameter and return value

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    [ReactiveCommand]
    private string Execute(string parameter) => parameter;
}

Usage ReactiveCommand with parameter and async return value

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    [ReactiveCommand]
    private async Task<string> Execute(string parameter) => await Task.FromResult(parameter);
}

Usage ReactiveCommand with IObservable return value

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    [ReactiveCommand]
    private IObservable<string> Execute(string parameter) => Signal.Emit(parameter);
}

Usage ReactiveCommand with CancellationToken

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    [ReactiveCommand]
    private async Task Execute(CancellationToken token) => await Task.Delay(1000, token);
}

Usage ReactiveCommand with CancellationToken and parameter

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    [ReactiveCommand]
    private async Task<string> Execute(string parameter, CancellationToken token)
    {
        await Task.Delay(1000, token);
        return parameter;
    }
}

Usage ReactiveCommand with CanExecute

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    private IObservable<bool> _canExecute;

    [Reactive]
    private string _myProperty1;

    [Reactive]
    private string _myProperty2;

    public MyReactiveClass()
    {
        _canExecute = this.WhenAnyValue(x => x.MyProperty1, x => x.MyProperty2, (x, y) => !string.IsNullOrEmpty(x) && !string.IsNullOrEmpty(y));
    }

    [ReactiveCommand(CanExecute = nameof(_canExecute))]
    private void Search() { }
}

Usage ReactiveCommand with property Attribute pass through

using ReactiveUI.SourceGenerators;

public partial class MyReactiveClass
{
    private IObservable<bool> _canExecute;

    [Reactive]
    private string _myProperty1;

    [Reactive]
    private string _myProperty2;

    public MyReactiveClass()
    {
        _canExecute = this.WhenAnyValue(x => x.MyProperty1, x => x.MyProperty2, (x, y) => !string.IsNullOrEmpty(x) && !string.IsNullOrEmpty(y));
    }

    [ReactiveCommand(CanExecute = nameof(_canExecute))]
    [property: JsonIgnore]
    private void Search() { }
}

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 string to the IViewFor Attribute. 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).
using ReactiveUI.SourceGenerators;

[IViewFor(nameof(MyReactiveClass))]
public partial class MyReactiveControl : UserControl, IViewFor<MyReactiveClass>
{
    public MyReactiveControl()
    {
        InitializeComponent();
        ViewModel = new MyReactiveClass();
    }
}

[IViewFor] generates the ViewModel property and the IViewFor<T> implementation. It does not register the view. ReactiveUI.Binding's generator adds each class that declares IViewFor<T> to the generated view lookup. That generator cannot see the interface [IViewFor] adds, so list IViewFor<T> on the class yourself, as above.

Platform specific Attributes

WinForms

RoutedControlHost

using ReactiveUI.SourceGenerators.WinForms;

[RoutedControlHost("YourNameSpace.CustomControl")]
public partial class MyCustomRoutedControlHost;

ViewModelControlHost

using ReactiveUI.SourceGenerators.WinForms;

[ViewModelControlHost("YourNameSpace.CustomControl")]
public partial class MyCustomViewModelControlHost;