reactiveui-un-framework-mvvm-para-net-basado-en-reactivex

ReactiveUI, an MVVM framework for .NET based on ReactiveX

  • 12 min

Today we are going to look at ReactiveUI, a powerful framework for implementing the MVVM pattern in .NET applications, based on the Observable-Observer pattern.

In the previous post we saw MVVM Light as a simple and non-intrusive alternative with the necessary helpers to implement an MVVM pattern.

This time we will look at the powerful ReactiveUI, which is more than just a framework it almost represents a change in philosophy when it comes to programming.

ReactiveUI is available for WinForms (with somewhat limited functionality), WPF, UWP, and Xamarin Forms applications. It is Open Source and its code is available at this link.

It must be said that ReactiveUI is a framework with a steep learning curve. Furthermore, the documentation is relatively scarce, and many tutorials refer to older versions.

Nevertheless, its enormous power makes it a really interesting framework to learn and incorporate into our projects. Let’s see some of the features of this interesting .NET framework.

Why ReactiveUI?

ReactiveUI is based on the well-known ReactiveX, a multi-language library to facilitate asynchronous work and event management, which is based on the best of the Observer-Observable pattern, iterators, and functional programming.

ReactiveX bases its operation on the intensive use of Observables. An Observable is an object that emits notifications asynchronously and which are received by Observers or subscribers, following a PubSub pattern.

Observables represent a shift to a “Push” philosophy, as opposed to an iterator which represents a “Pull” philosophy. So, typically in an iterator we loop through a collection performing actions on each element. In contrast, when using an Observable, it emits notifications when appropriate, and the Observers execute the actions.

Furthermore, they represent a better approach to a functional and declarative software definition versus the classic imperative definition. Instead of ‘doing something’, we declare the relationships in the constructor. This has advantages, especially from the point of view of scalability and maintainability.

Observables in ReactiveX

The operation of Observables in ReactiveUI is the same as in ReactiveX, on which it is based. But ReactiveUI adds additional methods that facilitate their use in the UI, as we will see later. First, we will briefly review the use of Observables in ReactiveX.

As we said, an Observable is an object that emits notifications that are received by Observers following a PubSub pattern.

The methodology for working with Observables consists of:

  1. Generate an Observable
  2. Apply operators or filters to the emitted messages
  3. Consume the messages in the Observers

We can create Observables in many ways. As simple examples, we can create an Observable that emits numbers in a range,

IObservable<int> myObservable = Observable.Range(1, 10);
Copied!

Or an Observable that emits a notification every N seconds.

IObservable<int> myObservable = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
Copied!

Next, we use operators and filters on the notifications emitted by the Observables. We also have many methods available such as, for example, Debounce, Distinct, First, Last, SkipUntil, TakeUntil, TimeInterval, TimeOut, Merge, among many others.

Finally, we would subscribe to the notifications

timer.Subscribe(x => // doWhatEver));
Copied!

You have a list of available operators at http://reactivex.io/documentation/operators.html

Of course, we can also create our own Observables. For example, below we create an Observable that counts up to 100 every 10ms, then from 100 down to 0, and repeats indefinitely.

IObservable<long> loop(int i)
{
  var obs1 = Observable.Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(10)).TakeUntil(x => x > 100);
  var obs2 = Observable.Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(10)).TakeUntil(x => x > 100).Select(x => 100 - x);
  return obs1.Concat(obs2).Repeat();
}
Copied!

For more information on using ReactiveX, consult the relevant documentation on the project’s page.

Using ReactiveUI

Next, we will see some of the functionalities that ReactiveUI adds for using Observables in the UI.

INotifyPropertyChanged

One of the necessary points of any MVVM Framework is to facilitate an implementation of INotifyPropertyChanged. Indeed, ReactiveUI provides its own implementation, which we use as follows.

private string myProperty;  
public string MyProperty
{
    get { return myProperty; }
    set { this.RaiseAndSetIfChanged(ref myProperty, value); }
}
Copied!

Nothing new, just one of many possible implementations of INotifyPropertyChanged, which in the case of ReactiveUI is called RaiseAndSetIfChanged(…).

Observables and Properties

ReactiveUI goes far beyond a simple INotifyPropertyChanged, as it combines the power of Observables with the property Binding mechanism.

For this we have the methods:

  • WhenAny/WhenAnyValue, converts a property into an Observable.
  • ToProperty, converts an Observable into a property.

These two methods are part of the core of ReactiveUI, and we will use them frequently. Let’s delve into their operation.

WhenAny and WhenAnyValue

The WhenAny methods and its variant WhenAnyValue are used to create subscriptions to changes in a property in the VM. These extension methods create a new IObservable that fires a notification when the property is modified.

The WhenAny method uses an IObservedChange object, which contains the sender of the change (Sender), the expression of the change (Expression), and the value after the change (Value). That’s why WhenAny requires a selector as the second parameter.

this.WhenAny(x => x.MyProperty, x => x.Value)
Copied!

In most cases, the WhenAny syntax will have x.Value as the selector. Therefore, a simplified version WhenAnyValue is available, which is what we will use most of the time.

this.WhenAnyValue(x => x.MyProperty)
Copied!

WhenAny/WhenAnyValue allow monitoring changes in multiple properties simultaneously.

this.WhenAnyValue(x => x.Red, x => x.Green, x => x.Blue, 
                  (r, g, b) => new Color(r, g, b));
Copied!

Or in nested properties.

this.WhenAnyValue(x => x.Foo.Bar.Baz);
Copied!

ToProperty

The opposite case of WhenAny/WhenAnyValue, that is, project an Observable into a property. In many cases we will need to convert an Observable into a property, so that it can be bound to the UI.

For this, ReactiveUI provides the ToProperty(…) method and the auxiliary object ObservableToPropertyHelper. As their names indicate, both objects allow converting an Observable into a property.

For example, if we have a readonly property ‘Counter’, which we want to bind to the UI, and whose state is updated from an Observable, we would define the following.

private readonly ObservableAsPropertyHelper<string> counter;  
public string Counter  
{
    get { return counter.Value; }
}
Copied!

Where ObservableAsPropertyHelper is an object that responds to modification notifications from an Observable.

Later, in the VM constructor, we would perform the projection to the desired property, using the ‘ToProperty’ method.

public MyViewModel()  
{
    Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(2))
      .Select(x=> x.ToString())
      .ToProperty(this, vm => vm.Counter, out counter);
}
Copied!

Example of Properties and Observables

Let’s see all this in a simple example. We have two text properties modifiable by the user, Firstname and LastName, and a FullName property that depends on the two previous ones.

This is how we would define the properties:

private string firstName;  
public string FirstName  
{
    get { return firstName; }
    set { this.RaiseAndSetIfChanged(ref firstName, value); }
}

private string lastName;  
public string LastName  
{
    get { return lastName; }
    set { this.RaiseAndSetIfChanged(ref lastName, value); }
}

private readonly ObservableAsPropertyHelper<string> fullName;  
public string FullName  
{
    get { return fullName.Value; }
}
Copied!

On the other hand, in the VM constructor we would define the logic declaratively. In this example, we define an observable that responds to changes in FirstName and LastName, uses a selector to concatenate both properties, and finally projects them into the FullName property.

public MyViewModel()  
{
    this.WhenAnyValue(vm => vm.FirstName, vm => vm.LastName,
            (first, last) => $"{first} {last}")
            .ToProperty(this, vm => vm.FullName, out fullName);
}
Copied!

Simplification with FODY

As we can see, the syntax for ReactiveUI, while quite intuitive, requires a lot of repetitive code. This improves substantially if we use the FODY extension and the attributes available for ReactiveUI.

Thus, the previous example would become.

[Reactive] public string FirstName { get; set; }

[Reactive] public string LastName { get; set; }

[ObservableAsProperty] public string FullName { get; }

public MyViewModel()  
{
    this.WhenAnyValue(vm => vm.FirstName, vm => vm.LastName,
    (first, last) => $"{first} {last}")
    .ToPropertyEx(this, vm => vm.FullName);
}
Copied!

As we can see, much more concise and convenient.

Note that when using FODY, the property ‘ToProperty(…)’ becomes ‘ToPropertyEx(…)’, necessary because the generated ObservableAsPropertyHelper object is not defined until we compile and FODY creates the variable for us.

Commands

Of course, ReactiveUI implements ICommand, called ‘ReactiveCommand’. A command is declared using:

public ReactiveCommand<TParam, TResult> MyCommand {get;}
Copied!

Where TParam is the type of parameter the command receives, and TResult is the type of result it returns. If either, or both, is null, we will use the ‘Unit’ type provided by ReactiveUI, which for practical purposes we can consider similar to ‘void’.

Later, to create the Command in the VM constructor we use one of the following constructor methods,

  • CreateFromObservable() - Create from an Observable.
  • CreateFromTask() - Execute a Task with async/await.
  • Create() - Execute a synchronous Func or Action.
  • CreateCombined()

For example, to create a synchronous command with an int parameter we would declare it as follows,

public ReactiveCommand<int, Unit> CommandParemeterless { get; private set;}
Copied!

In the constructor we would create the instance with

CommandParemeterless = ReactiveCommand.Create<int, Unit>(
    integer => Console.WriteLine(integer)
);
Copied!

And we could execute it as follows,

CommandParemeterless.Execute(42).Subscribe();
Copied!

As another example, to create an asynchronous command without parameters we would declare it like this,

public ReactiveCommand<Unit, Unit> AsyncCommand { get; private set; }
Copied!

And in the constructor we would instantiate it as follows

AsyncCommand = ReactiveCommand.CreateFromTask(async () =>
  {
    await Task.Delay(2000);
    // do whatever
});
Copied!

Message BUS

Another common component in MVVM frameworks is the Message BUS, which of course is also included in ReactiveUI. Although its use should not be abused because it makes debugging difficult and can mask dependencies between classes, in certain cases it is useful for keeping coupling between classes weak.

Its use is similar to other notification service implementations. To subscribe to messages of a certain class we would use

MessageBus.Current.Listen<BusMessageDemo>().Subscribe(x => Console.WriteLine($"{x.Time} Recieved: {x.Content}"));
Copied!

On the other hand, another class would send messages through the BUS to the various subscribers.

MessageBus.Current.SendMessage(new BusMessageDemo { Content = "DemoMessage Content" });
Copied!

In this example we have used this auxiliary class, which in your project should be replaced by the corresponding messages in your application.

// Auxiliary class for Bus example
public class BusMessageDemo
{
  public DateTime Time { get; set; } = DateTime.Now;

  public string Content { get; set; }
}
Copied!

Service Location

Another common functionality in most MVVM frameworks is IoC and DI. ReactiveUI uses Splat as a service locator, a fast and cross-platform container. It is simple, but powerful enough for most uses.

To register a new service, the CurrentMutable property is used, which allows registering services with the following methods

// Creates a new service every time it is resolved
Locator.CurrentMutable.Register(() => new MyService(), typeof(IMyService));

// Returns the same object, existing (or created when registered)
Locator.CurrentMutable.RegisterConstant(new MyService(), typeof(IMyService));

// Returns an object, which is created the first time it is requested
Locator.CurrentMutable.RegisterLazySingleton(() => new MyService(), typeof(IMyService));
Copied!

Later, we can resolve the services using the Current property.

var myService = Locator.Current.GetService<IMyService>();
var allMyServices = Locator.Current.GetServices<IMyService>();
Copied!

Finally, ReactiveUI does not provide by itself a DI system to automatically resolve dependencies via constructor or property injection, although it is possible to combine it with an existing DI like AutoFac.

However, the recommended usage by the developers for resolving dependencies from the constructor is as follows.

public class Foo : IFoo
{
  IService MyService;
  
  public Foo(IService myService)
  {
    MyService = myService ?? Locator.Current.GetService<IService>();
  }
}
Copied!

Dispatcher

ReactiveUI also provides tools to simplify launching actions on the application’s UI Thread, via ‘Schedules’. Thus, the following example shows how to execute a task on the UI.

RxApp.MainThreadScheduler.Schedule(() => DoSomething());
Copied!

However, Schedules are normally integrated into the definition of the Observables themselves.

MyObservable.Throttle(TimeSpan.FromSeconds(0.8))
    .Where(...)
    .DistinctUntilChanged()
    .ObserveOn(scheduler)
    .Subscribe(x => { });
Copied!

Or for example

var result = await Observable.Start(() => {
    int number = ThisCalculationTakesALongTime();
    return number;
}, RxApp.TaskpoolScheduler);
Copied!

Reactive Collections

Finally, ReactiveUI also provides collections that complement ObservableCollections, for the use of collections of Observables. They are very powerful and their use covers many possibilities, so it is advisable to read the documentation of these objects.

A simple example would be the following, which creates a derivedList that would contain the elements of myList filtered according to their Status.

var myDerivedList = myList
    .ToObservableChangeSet()
    .Filter(t => t.Status == "someStatus")
    .AsObservableList();
Copied!

That concludes this presentation of ReactiveUI, a very powerful MVVM framework with endless possibilities. As we said at the beginning, more than a framework, ReactiveUI represents a change in programming philosophy.

The learning curve is steep but, fortunately, the adoption of ReactiveUI is very scalable. We don’t need to use everything this framework offers from the start. We can begin with the most basic functionalities, and gradually incorporate and take advantage of the more advanced ones.

In any case, it is worth taking a look because for years these types of patterns have been the usual trend in programming, across different languages and platforms, both in desktop and web applications.