Language: EN

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

ReactiveUI, an MVVM framework for .NET based on ReactiveX

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

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

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

ReactiveUI is available for WinForms applications (with somewhat limited features), WPF, UWP, and Xamarin Forms. 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. In addition, the documentation is relatively scarce, and many of the tutorials refer to previous versions.

However, 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 framework for .NET.

Why ReactiveUI?

ReactiveUI is based on the well-known ReactiveX, a multilanguage library to facilitate asynchronous work and event management, 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 that are received by Observers or subscribers, according to a PubSub pattern.

Observables represent a shift to a “Push” philosophy, as opposed to an iterator that represents a “Pull” philosophy. Thus, typically in an iterator, we traverse a collection performing actions on each element. On the contrary, when using an Observable, it emits notifications when appropriate, and Observers execute the actions.

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

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 according to a PubSub pattern.

The methodology for working with Observables consists of:

  • Generating an Observable
  • Applying operators or filters to the emitted messages
  • Consuming 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);

Or an Observable that emits a notification every N seconds.

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

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

Finally, we would subscribe to the notifications

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

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, and then from 100 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();
}

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

Using ReactiveUI

Next, we will see some of the functionalities that ReactiveUI adds to use 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); }
}

Nothing new, simply an implementation of many possible INotifyPropertyChanged that in the case of ReactiveUI is called RaiseAndSetIfChanged(…).

Observables and properties

ReactiveUI goes much further than 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 how they work.

WhenAny and WhenAnyValue

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

The WhenAny method uses an IObservedChange object, which contains the change emitter (Sender), the change expression (Expression), and the value after the change (Value). Therefore, WhenAny requires a selector as a second parameter.

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

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

this.WhenAnyValue(x => x.MyProperty)

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));

Or in nested properties.

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

ToProperty

The opposite case of WhenAny/WhenAnyValue, that is, projecting an Observable onto a property. Many times, we will need to convert an Observable into a property, so that it can be bound to the UI.

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

For example, if we have a read-only 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; }
}

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

Subsequently, in the constructor of the VM, we would project to the property we want, 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);
}

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 previous two.

We would define the properties as follows:

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; }
}

On the other hand, in the constructor of the VM, 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 onto the FullName property.

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

Simplification with FODY

As we can see, the syntax for ReactiveUI, while quite intuitive, requires a lot of repetitive code. This substantially improves 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);
}

As we can see, much more concise and convenient.

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

Commands

Of course, ReactiveUI provides an implementation of ICommand, called ‘ReativeCommand’. The declaration of a command is done through:

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

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

Subsequently, to create the Command in the constructor of the VM, 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;}

In the constructor, we would create the instance with

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

And we could execute it as follows,

CommandParemeterless.Execute(42).Subscribe();

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

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

And in the constructor, we would instantiate it as follows

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

Message BUS

Another common component in MVVM frameworks is the Message BUS, which is also included in ReactiveUI. While it is not advisable to abuse its use because it complicates debugging and can mask dependencies between classes, in certain situations it is useful to keep the coupling between classes weak.

Its use is similar to the rest of the notification service implementations. To subscribe to messages from a certain class, we would use

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

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

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

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; }
}

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));

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

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

Finally, ReactiveUI does not provide a DI system itself 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 to resolve dependencies from the constructor is as follows.

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

Dispatcher

ReactiveUI also provides tools to simplify dispatching actions on the UI thread of the application, through ‘Schedule’. Thus, the following example shows how to execute a task on the UI.

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

However, the Schedule is normally integrated in the definition of the Observables themselves.

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

Or for example

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

Reactive Collections

Finally, ReactiveUI also provides collections that complement ObservableCollection, 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();

So far 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 the programming philosophy.

The learning curve is steep but, fortunately, the adoption of ReactiveUI is very scalable. It is not necessary to use everything that this framework offers from the beginning. We can start 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 this type of patterns has been the usual trend in programming, in different languages and platforms, both in desktop and web applications.