Reactive Extensions + WPF? Yes, Please

excited

In my recent post on IObservables, I discussed augmenting the ReactiveExtensions library with the ability to “cache” a Select() call. One of the ways I am using this in my current project is to provide the same data to multiple UI elements. Sadly, WPF does not directly support IObservable for data binding. However, with a little effort you can use IObservable very effectively as a data source for your ViewModels.

My Solution

There are a few libraries out there (such as ReactiveUI) that aim to help with this task. However I found that they did not sufficiently simplify my most common case: a property of type T which updates based on an IObservable. I wanted this binding to be incredibly simple, one line if possible. I also wanted it to be robust — that is, you can’t accidentally bind a property of string to an IObservable<int>.

After poking around for a bit, I ended up with a solution I think works quite well. I added a function to my base ViewModel (a subclass of MVVMLight‘s ViewModelBase):

void BindPropertyToObservable(Expression> property, IObservable obs)

In practice, this looks like the following:

struct MyViewDataSource {
    public IObservable Numerology;
    public IObservable Textalot;
}

class MyViewModel : ProjectViewModelBase
{
    public int Numerology { get; private set; }
    public string Textalot { get; private set; }

    public MyViewModel(MyViewDataSource source)
    {
        Numerology = 347;
        Textalot = "Sensible Default";

        BindPropertyToObservable(() => Numerology, source.Numerology);
        BindPropertyToObservable(() => Textalot, source.Textalot);
    }
}

Looks pretty slick, huh?

Potential Objections

I’m guessing a few people might already have a few concerns about this.

  1. What about the subscriptions?
    Good question. Internally, BindPropertyToObservable produces an IDisposable that represents our subscription to the IObservable. That IDisposable is saved in a list in the base class, and the base class exposes a method to subclasses which allows for de-subscribe / re-subscribe.
  2. How are values updated?
    I’m capturing an Expression<Func<T>>, which is a fancy way of saying I have access to the abstract syntax tree of an expression that returns a value of type T. Because the expression refers to a property, I am able to use reflection to get both the ability to change the value and the name of the property (which I need to implement INotifyPropertyChanged, and force a UI update).
  3. But wait — reflection is really slow, isn’t it?
    It can be. But that’s why I only use it on the initial bind. I only need to get the property name once, and then I hold on to it for when a new value comes in. That’s the easy part. For setting the value, I found a blog post about how to create a delegate which will set the value, given a PropertyInfo:

    var prop = type.GetProperty(name, BindingFlags.NonPublic 
                   | BindingFlags.Public | BindingFlags.Instance);
    var setMethod = prop.GetSetMethod(true);    
    var setAct = (Action)Delegate.CreateDelegate(typeof (Action), 
                     this, setMethod);
    

    This delegate is basically the “compiled” version of setting the property, so we don’t have to reflect every time the value changes. Instead, it just calls setAct(newValue).

You might have noticed that I bundled the IObservable objects for my view model together in a lightweight struct rather than passing them each in as arguments. There are a few reasons for this.

  1. I’m avoiding connascence of position.
  2. These data points are presumably related (otherwise why are they on the same ViewModel), so I’m providing some encapsulation, even if it isn’t strictly enforced.
  3. Most of my “data sources” (structs full of IObservable objects) are built by some factory type class. Because the logic of how that data is provided is kept in a stateless, single-responsibility object, it is easy to rearrange where the data goes. For example, say I want to give another ViewModel access to the same information. All I have to do is add that data source to its constructor.
  4. The data sources could ultimately feed any type of interface, not just WPF. Say for example that I wanted to expose the latest data via a JSON API. All I would need to do is use the IObservable objects to create a JSON object that is always up to date. Then when a request comes in, I just need to send the JSON object.

“Ultra-thin” ViewModels

I’ve become convinced that in a Reactive world, you want your ViewModels as simple as possible. Yes, you could perform Reactive extension methods like Select, Where, and Buffer inside your constructor before binding to properties, but then that functionality is bound to your ViewModel and complicates its tests. Basically it is conflating state and functionality, a practice to which I am increasingly opposed.

There are a few design benefits to this “ultra-thin ViewModel” concept as well. As seen in my example, you can give a sensible default to the data, which makes it easy to test out a view with different data. Using this style, ViewModels become simple enough that a designer that knows WPF/XAML but not C# can probably create the ViewModel (or at least its shell) in conjunction with creating the View, potentially saving some time if the programmer is busy with other tasks or if the programmer and designer are working semi-independently.

Overall, I’m loving the Reactive Extensions library, and I think my current UI would be almost unmanageable without it. I highly recommend it to anyone dealing with streaming data analysis.
 

Conversation
  • kariantti says:

    Do you have BindPropertyToObservable codes available somewhere?

  • Comments are closed.