Skip to content

Instantly share code, notes, and snippets.

@anaisbetts
Created August 27, 2012 08:54
Show Gist options
  • Save anaisbetts/3486765 to your computer and use it in GitHub Desktop.
Save anaisbetts/3486765 to your computer and use it in GitHub Desktop.

ReactiveUI 4.0 Preview is live!

After coding some pretty interesting new features, as well as to update to the latest released version of Rx (2.01), I am now releasing a preview release of ReactiveUI 4.0. You can get the binaries one of two ways:

  • install-package reactiveui -pre or install-package reactiveui-winrt -pre
  • Download the Zip release from GitHub

How does this break backwards compatibility?

A few types have been renamed from 3.x (IViewForViewModel => IViewFor), a few deprecated methods have been removed (CollectionExtensions), and the biggest one is that NLog is now optional - if you want to use it, make sure to install the reactiveui-nlog package (it comes by default in the reactiveui metapackage)

WhenAny now works with any object!

WhenAny and ObservableForProperty now work with any .NET object. The runtime will detect what kind of object it is (DependencyObject, INPC, etc) and will determine how to get notifications from it. Note that if you use WhenAny with an object that doesn't notify (like a regular .NET object), it will warn you on the logger that changes won't be reported. This framework is extensible as well, so on MonoMac for example, notifications for NSObjects will be derived using Key-Value Observing (KVO).

This means that the old ObservableForDP method is now deprecated - use WhenAny instead.

View Bindings: A Compelling Replacement for XAML bindings

There are currently two methods that are used in the XAML world for binding Views to ViewModels. The original method, XAML Bindings (i.e. {Binding Foo}), is quite flexible, but a frustrating aspect of it is that Bindings that are incorrect, due to typos or other mistakes, don't generate errors - you only see the error in the trace output. The Binding syntax can also get quite verbose, and isn't always easy to verify because paths are relative to the "nearest" DataContext set.

Caliburn Micro decided to take a different approach, with their "Convention-Based Wireup" - this approach decides to automatically wire up named controls with the properties of the same name (even being smart enough to realize that the most "common" property of TextBox is the Text property). While this approach is much more "wrist friendly", it suffers from the same type of run-time failures: CM cannot know whether a binding should exist or not.

ReactiveUI is presenting a new approach: wiring up controls via a declarative syntax. This has a number of advantages:

  • It's still wrist-friendly
  • Bindings are clearly described in one place
  • Bindings that are never valid (i.e. because the property name has changed) break the build.
  • This binding syntax isn't tied to Xaml - you can effectively use ReactiveUI bindings with any UI framework with a bit of work, including Cocoa (MonoMac) and GTK#.

Consider the following ViewModel, which is valid RxUI 3.x code as well:

  public class MainPageViewModel : ReactiveObject
  {
      string _SomeText = "";
      public string SomeText {
          get { return _SomeText; }
          set { this.RaiseAndSetIfChanged(x => x.SomeText, value); }
      }

      ObservableAsPropertyHelper<string> _FooMirror;
      public string FooMirror {
          get { return _FooMirror.Value; }
      }

      public ReactiveCommand Ok { get; protected set; }

      public MainPageViewModel()
      {
          this.WhenAny(x => x.SomeText, x => x.Value)
              .Select(x => "Foo" + x ?? "")
              .ToProperty(this, x => x.FooMirror);

          Ok = new ReactiveCommand();
      }
  }

In ReactiveUI 4.0, this is the way to wire up the View:

  public partial class MainPage : Page, IViewFor<MainPageViewModel>
  {
      public MainPage()
      {
          ViewModel = new MainPageViewModel();
          this.InitializeComponent();

          // Connect the ViewModel's SomeText to the View's SomeText.Text
          this.Bind(ViewModel, x => x.SomeText, x => x.SomeText.Text);

          // Since the View half isn't specified here, it will default to the
          // control with the same name, and use the most "reasonable"
          // property, just like Caliburn Micro
          this.OneWayBind(ViewModel, x => x.FooMirror);

          // Ditto with Commands, find a Control named "Ok"
          this.BindCommand(ViewModel, x => x.Ok);
      }

      object IViewFor.ViewModel {
          get { return ViewModel; }
          set { ViewModel = (MainPageViewModel) value; }
      }
      public MainPageViewModel ViewModel { get; set; }
  }

This feature is a work in progress, and there are definitely some cases that need to be smoothed out. However, I'd love feedback on whether this is useful or not.

@distantcam
Copy link

For the binding, what about this instead?

public partial class MainPage : Page
{
    private static BindingManager Binder = BindingManager.FromViewModel<MainPageViewModel>();

    public MainPage()
    {
        ...

        Binder.Bind(x => x.SomeText, x => x.SomeText.Text);

        Binder.OneWayBind(x => x.FooMirror);
    }
}

@shiftkey
Copy link

 public MainPage()
  {
      ViewModel = new MainPageViewModel();
      this.InitializeComponent();

      ...
  }

Is there a reason (aside from convenience) why the ViewModel is populated here?

Are there going to be any issues with me changing the DataContext/ViewModel on the fly with these bindings?

@justinmchase
Copy link

Why not have someone inherit from View instead of apply IViewFor<> to a Page? Then you could put those Bind methods right on View and drop the first parameter.

@devuxer
Copy link

devuxer commented Aug 27, 2012

This looks like a pretty clever solution, but I'm not sure the benefits outweigh the costs. I find that the standard binding syntax only gets verbose when trying to bind to properties of, say, ancestor controls in the visual tree, not to properties of the view model. {Binding Property} or {Binding Property.SubProperty} is not a lot to type. Your solution appears to make things more verbose, especially if you consider that you must provide a name for each control that participates in a binding. Your solution does buy you Intellisense support, the ability to use refactor-rename (albeit in two steps), and, most importantly, compile-time checking of names, but I'm not sure those advantage are worth the additional typing and the fact that you must now coordinate things using three files (view model, view, and code behind) instead of just two (view model and view). All that said, I'm not sure a much better solution exists without Microsoft open sourcing the codebase (or tackling the problem itself).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment