Binding to Observables in XAML - Part 1

This post is a general post about making an observable Binding in XAML. I have a detailed blog about the implementation for the following platforms:

These are independent, so you don't need to read the first to understand the rest.

You can find all the code here.

Motivation

I love observables in both .NET and in JavaScript. But I feel the JavaScript community, particulary Angular, has embraced this idea a lot more. In Angular you can immediately bind to the values produced by an observable by using an AsyncPipe:

<span>{{name$ | async}}</span>

In the example above name$ is the observable. The AsyncPipe takes care of subscribing to and unsubscribing from the observable. That means the component doesn't have to worry about the life cycle and can simply expose observables.

I felt like this should be possible with XAML as well. Although there are nice projects like Reactive UI, they seem to have a different approach than what I had in mind.

The Goal

I want to achieve something like this:

View

<TextBlock Text="{ObservableBinding Name, Mode=OneWay}"/>

ViewModel

public IObservable<string> Name { get; set; }

Where the ObservableBinding takes care of subscribing and unsubscribing. Here, any new value from the ViewModel is pushed to the View. The Name property doesn't have to implement INotifyPropertyChanged because new values arrive through the observable.

The previous part emulated the behavior of the AsyncPipe. But since we're here, we can take it up a notch and try to create something like:

View

<TextBox Text="{ObservableBinding Name, Mode=OneWayToSource}"/>

ViewModel

public IObserver<string> Name { get; set; }

This time the values are pushed from the View to the ViewModel.

why_not_both.jpg

When pushing values in both directions, you get something like this:

View

<TextBox Text="{ObservableBinding Name, Mode=TwoWay}"/>

ViewModel

public Subject<string> Name { get; set; }

Two key elements for the implementation are performance and usability. I don't mind using Reflection to set up the Binding. But once it's there, it needs to perform well.

Candidates

We need a mechanism to subscribe and unsubscribe from these observables. These are my four candidates:

  • Converter
  • Behavior
  • Attached Property
  • MarkupExtension

Converter

The converter is the most similar to the AsyncPipe in angular. In the best case it would look something like this:

<TextBlock Text="{Binding Name, Converter={StaticResource Async}}"/>

However, I found that it's very difficult to get a reference to the Control (TextBlock in this case) from the IValueConverter. I quickly abandoned this path. IValueConverters are meant to return one value immediately.

Behavior

Behaviors are the typical go-to solution when you want to do something in XAML that you normally can't. I found that subscribing and unsubscribing are very easy since you have an OnAttached() and OnDetaching() function. After some experimentation I got something like this:

<TextBlock>
  <i:Interaction.Behaviors>
    <local:ObservableText Stream="{Binding Name}"/>
  </i:Interaction.Behaviors>
</TextBlock>

It worked, and it's not too bad (although I hardcoded it for the Text property). But it was a bit too lengthy in my opinion. Imagine having to write this for every binding you require. So I didn't even try to make this more generic and moved on to the next candidate.

Attached Properties

Through my years of working with XAML, I noticed that you can use Attached Properties to achieve something similar to behaviors. After some work I got something like this:

<TextBox o:Bind.Text="{Binding SomeObservable}"
         o:Emit.Text="{Binding SomeObserver}" />

Which is nice and convenient. Bind gives us Oneway Binding. Emit gives us OneWayToSource. And if you want both? Well, just add both an refer to the same Subject. The major downside here is that I have to create a Text property and a Value property and every property I want to bind to. Which is a lot of work.

I did choose this path for UWP (see detailed blog here). The main reason is that UWP does not support custom MarkupExtensions.

MarkupExtension

A custom MarkupExtension turned out to be the best option for both WPF (details here) and Xamarin.Forms (details here). I ended up with something like this:

<TextBox Text="{o:Bind SomeObservable, Mode=OneWay}"/>
<TextBox Text="{o:Bind SomeObserver, Mode=OneWayToSource}"/>
<TextBox Text="{o:Bind SomeSubject, Mode=TwoWay}"/>

Where Mode is not mandatory.

Next Steps

There are some low hanging fruits to pick here. Implementing Converters and StringFormat will probably be quite easy. A bigger challenge is avoiding Reflection all together. This would involve creating something like UWP's {x:Bind}. This requires generating specific code on build. But let's save that for another day.

Once again you can find all the code (including the abandoned paths) here.