Building a ReactiveUi

By: on March 31, 2015

I’d be one of the first few to admit that I am horrible at making UIs. My rather positive experience with working with Knockout made me wonder one day if there was an equivalent in the .NET world where I could piece together a quick application with an actual UI, while wiring up the display elements to automatically update from their backing data source.

Enter ReactiveUi which is used in the Github for Windows client. ReactiveUi is based on the Reactive Extensions. ReactiveUi is supported for both Windows Forms and Windows Presentation Foundation. I used to remember hearing about some rosy picture being painted about Windows Presentation Foundation (WPF) back in the days of Windows Vista, and I thought I might as well learn to use it at the same time. As I soon came to find out, WPF has been stagnant for quite a while and is not exactly pleasant to use. (But that’s not the point of the post.)

With this in mind, I set out to build an application written in C# using WPF backed by ReactiveUi. The difficulties encountered with this small project were three-fold: unfamiliarity with WPF, ReactiveUi and the Reactive Extensions. This was not helped by the sparseness of ReactiveUi’s documentation.

Model View ViewModel

ReactiveUi uses the MVVM pattern. In my application, my models were instantiated as empty objects that contained nothing but an identifier. The data in the models have to be populated, perhaps asynchronously, by a call to an external API.

The ViewModel is where ReactiveUi comes in. ViewModel objects are meant to turn properties on models into a bunch of Observables where you can bind controls on your UI to. ReactiveUi facilitates this creation of these observables.

Finally, the View, which is usually a WPF or Windows Forms control, has to bind to properties on the ViewModel. Once again, ReactiveUi facilitates this by allowing the creation of one or two way binding. This concept is quite similar to Data Binding in WPF, albeit done via ReactiveUi instead.

ViewModel

The ViewModel is probably the heart of a ReactiveUi application. A ViewModel should be a specialisation of a ReactiveObject. ReactiveObject implements INotifyPropertyChange so that you don’t have to, and gives access to some helper extension methods to create Observables. Your ViewModel would then be able to turn some property on your Model, or even compose several properties into an Observable. An Observable will notify its subscribers of changes in the property.

In its simplest form, your ViewModel might have some property that should notify its subscribers in case of any value change. In that case, the code is no more than the following:

private int quantityYield;

public int QuantityYield
{
    get { return quantityYield; }
    set { this.RaiseAndSetIfChanged(ref quantityYield, value); }
}

If your property is readonly, but calculated from some other property, you can make use of the `ObservableAsPropertyHelper` object:

private readonly ObservableAsPropertyHelper<bool> populated;

public bool Populated
{
    get { return populated.Value; }
}

public ViewModel() {
    // x is 'this'
    this.WhenAnyValue(x => x.Model.Populated)
        .ToProperty(this, x => x.Populated, out populated);
}

More complicated scenarios can also be achieved. Consider that I have a list of ingredients with quantities and pricing being variable, I would like an Observable on the total price of the ingredients. We can make use of `Observable.Merge` to compose several Observables.

using System;
using System.Reactive;
using System.Reactive.Linq;
using PromotionViabilityWpf.ViewModel;
using ReactiveUI;

namespace Foobar.ViewModel
{
    public class ViewModel : ReactiveObject
    {
        // The model in our example
        public Model Model { get; private set; }

        public ViewModel(Model Model)
        {
            Model = Model;

            // Data in the model is populated asynchroously. We need to know
            // when the data is populated
            this.WhenAnyValue(x => x.Model.Populated)
                .ToProperty(this, x => x.Populated, out populated);

            // The list of ingredients will not change, but the price
            // and quantity might. We build a ReactiveList of the ingredients
            // wrapped in a ViewModel with the appropriate Observable properties
            Ingredients =
                new ReactiveList<IngredientViewModel>(Model.Ingredients) { ChangeTrackingEnabled = true };

            // Initialise to zero
            IngredientsCost = 0;
            var ingredientsObservables = new[]
            {
                Ingredients.ItemChanged.Select(_ => Unit.Default),
                Ingredients.ShouldReset.Select(_ => Unit.Default),
                this.WhenAnyValue(x => x.Quantity).Select(_ => Unit.Default)
            };
            Observable.Merge(ingredientsObservables)
                .Where(_ => Model.Populated)
                .Subscribe(_ => IngredientsCost = Model.CostOfIngredients(Quantity));
        }

        public readonly ReactiveList<IngredientViewModel> Ingredients;

        private Coin ingredientsCost;

        public Coin IngredientsCost
        {
            get { return ingredientsCost; }
            private set { this.RaiseAndSetIfChanged(ref ingredientsCost, value); }
        }

        private readonly ObservableAsPropertyHelper<bool> populated;

        public bool Populated
        {
            get { return populated.Value; }
        }

        // Simplified quantity
        private int quantity;

        public int Quantity {
            get { return quantity; }
            set { this.RaiseAndSetIfChanged(ref quantity, value); }
        }
    }
}

When composing the Observables, notice that we had to make use of LINQ’s `Select` extension method to change the type of the individual Observables to `IObservable` for composition purposes. After composition, we also included a call to LINQ’s `Where` extension method to ensure that the model is populated before we perform any calculation. We then `Subscribe` to the merged Observable to recalculate the cost of the ingredients whenever a change is reported.

View

The View usually corresponds to a Control in WPF, or a form in Windows Form. To take advantage of ReactiveUi, your view must implement the `IView` interface, where `TViewModel` is the type of your ViewModel. You will then have access to some binding helper extension methods.

Consider the ViewModel above. An associated View might look like this:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ReactiveUI;

namespace PromotionViabilityWpf.View
{
    /// <summary>
    /// Interaction logic for PromotionView.xaml
    /// </summary>
    public partial class View : IViewFor<ViewModel>
    {
        public View()
        {
            InitializeComponent();

            // Two way bind between read-write property on ViewModel
            // and a textbox on the View
            this.Bind(ViewModel, vm => vm.Quantity, x => x.Quantity.Value);
            // Read-only binding between a DataGrid on a view and a list in the ViewModel
            this.OneWayBind(ViewModel, vm => vm.Ingredients, x => x.Ingredients.ItemsSource);
            // Read-only binding for a text box
            this.OneWayBind(ViewModel, vm => vm.IngredientsCost, x => x.IngredientsCost.Text);
        }

        object IViewFor.ViewModel
        {
            get { return ViewModel; }
            set { ViewModel = value as ViewModel; }
        }

        public ViewModel ViewModel { get; set; }
    }
}

In the code example above, we can see that we have attempted to bind a list of objects as the `ItemsSource` for a WPF `DataGrid` control. The nice thing about ReactiveUi is that it takes care of `DataTemplate` for the `DataGrid` too.

One thing you have to note is that ReactiveUi uses Splat to perform Inversion of Control through service location. When you use code similar to what I have presented above, ReactiveUi will attempt to find a class implementing `IView` to build the `DataTemplate` used to display each item in the ingredients list. You will have to add the following line of code somewhere in your application, possibly at its entry point:

Locator.CurrentMutable.Register(() => new IngredientViewModel(), typeof(IViewFor<IngredientViewModel>));

What I have done so far has only scratched the surface of ReactiveUi. The library offers some other nicer features that I have not touched yet. Due to the lack of documentation, it’s a bit difficult to discover those features. I will come back in the future with more posts talking about some “gotchas” and more features of the library.

Some conclusion

I started this small project to try and replicate the ease with which I could use Knockout in web pages to the GUI in desktop applications. So far, most of the difficulties I have grappled with have to do with my lack of familiarity and perhaps cumbersomeness of WPF rather than ReactiveUi. I do believe that ReacitveUi has, in some instances, made life slightly easier when coding the GUI. If the documentation were more robust, I could have saved some frustration and time. ReactiveUi is nothing like Knockout, other than the concept. For example, you cannot simply stick a `ko.observable` equivalent in ReactiveUi and call it a day.

It was fun, and if I were to do it again, I might just skip the WPF part.

Share

Comment

  1. Lucas says:

    Could you share the source, please?

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*