Skip Navigation LinksHome > View Post

And now for something completely crazy: Binding without WPF

I was struggling with a rather complex ViewModel (from Model-View-ViewModel or MVVM) that was actually composed of a number of ViewModels.

Most of the problems was internal notification of change - i.e one ViewModel wanted to know when another ViewModel changed. Sounds crazy but I'm comfortable it was a valid scenario. Because manually wiring up to INotifyPropertyChanged is about as much fun as nail-varnishing your eyeballs, I often work around the problem with a series of back references. However, it dawned on me that what I really want is WPF's awesome binding functionality. You know, it can bind deep on the properties of properties or properties and you don't have to worry about releasing event handlers to avoiud memory leaks.

If only we could use it without WPF - right in our ViewModels. And so my crazy experiment began...

From my brief foray into the internals of Binding (using Reflector and some contacts in the WPF product group) it seems that Binding is pretty coupled to DependencyProperties and there's no obvious way to use it without them. However, I managed to get my scenario working with the (semi-proud unveiling) of the BindingObject, which is used like this:

BindingObject bo = new BindingObject(sourceObject, "Property.That.IWant.ToBindTo", e => Changed(e));

And when the 'deep' property "Property.That.IWant.ToBindTo" on the sourceObject changes, the 'Changed' method is fired. The BindingObject is disposable (IDisposable) and this releases the references and unsubscribes the delegate so that everything can get Garbage Collected. Here's my slightly pokey looking implementation.

public class BindingObject : DependencyObject, IDisposable
{
    private bool _suppress;
    private Action<DependencyPropertyChangedEventArgs> _onChanged;

    public BindingObject(object source, string bindingPath, Action<DependencyPropertyChangedEventArgs> onChanged)
    {
        using (SuppressNotifications())
        {
            _onChanged = onChanged;

            Binding binding = new Binding(bindingPath);
            binding.Source = source;
            BindingOperations.SetBinding(this, ValueProperty, binding);
        }
    }

    public object Value
    {
        get { return (object)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }


    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(BindingObject), new UIPropertyMetadata(null, ValueChangedCallback));

    private static void ValueChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var instance = (BindingObject)d;
        Action<DependencyPropertyChangedEventArgs> onChanged = instance._onChanged;
        if (onChanged != null && !instance._suppress)
        {
            onChanged(e);
        }
    }

    public void Dispose()
    {
        BindingOperations.ClearBinding(this, ValueProperty);
        _onChanged = null;
    }

    public IDisposable SuppressNotifications()
    {
        return new Supresser(this);
    }

    private class Supresser : IDisposable
    {
        private BindingObject _bindingObject;

        public Supresser(BindingObject bindingObject)
        {
            _bindingObject = bindingObject;
            _bindingObject._suppress = true;
        }

        public void Dispose()
        {
            _bindingObject._suppress = false;
        }
    }
}

However pokey it looks though, it dramatically simplified the scenario I was tackling. It works in unit tests (with the total absence of WPF) too.

Thoughts please?

Tags: WPF C# MVVM

 
Josh Post By Josh Twist
12:55 PM
09 Mar 2009

» Next Post: Using base types in WCF
« Previous Post: TF03177: Team Project Creation Failed

Comments are closed for this post.

Posted by FallenGameR @ 10 Mar 2009 9:02 AM
Why do we need to suppress onChanged action in constructor?

I see no way BindingOperations.SetBinding could mess with delegate assigment. When we are in ValueChangedCallback doesn't that mean that binding is already successfull? Why bother with suppression?

Posted by Nikhil Kothari @ 10 Mar 2009 1:42 PM
This looks like a great start!

I've been having similar thoughts as we think about how view models compose, propagate change notifications etc.

Posted by Josh @ 12 Mar 2009 11:34 PM
@FallenGameR,

I found that the binding was firing as I hooked it up so decided to suppress as I didn't feel this would be the expected behavior for my class.

@Nikhil,

Thanks - I look forward to seeing it in System.ComponentModel ;)

Posted by Glenn Block @ 13 Mar 2009 1:46 AM
Hey Josh.

This looks great! We've been doing quite a bit of thinking on what we can do to simply the whole binding / notification experience, and make the code cleaer.

I like the binding object abstraction and the delegate based approach for the change notification, as an additional benefit is that it gives greater testability.

The latch mechanism for suppressing change notification is also very nice.

The one downside i see from the weakly type string prop name is that the compiler can't help you / you can't leverage refactoring tools. Have you thought of making it an expression?


Can you share a sample view model so we can see how you are using it within?


Posted by Pradeep Mahdevu @ 16 Mar 2009 10:38 PM
This is really cool!

Posted by Pradeep Mahdevu @ 14 Apr 2009 9:07 PM
Did you consider using Mediator Pattern? I was going thru josh smiths blog http://joshsmithonwpf.wordpress.com/ where he mentioned this. Since I use Composite application guidance, we tackle this using Eventaggregator.

In one of the places when i encountered the same problem and did not want to over use eventaggregator, we resolved this by having multiple views talking to the same presentation model and Strategy pattern ! Also, ur blog is awesome! :)

Posted by Josh @ 15 Apr 2009 12:50 AM
I too use Prism and the EventAggregator however, I'd prefer to bind to the ViewModel in the way I describe above because there's less friction - I don't have to change the ViewModel.

Also, and perhaps most importantly, if I bind to a property like so:

new BindingObject(sourceObject, "SomeObject.SomeProperty.SomeValue", e => Changed(e));

Then even if SomeObject's SomeProperty is changed, I'll be bound to the SomeValue of the new SomeProperty object. This scenario is *much* more complicated to do it with back-references/brokers/mediators. It's a doddle with WPF's binding engine though.

PS - thanks for the compliment :)

Posted by Brian Genisio @ 07 Jul 2009 4:20 AM
This is a neat way to work around the fact that your ViewModels are not DependencyObjects, but now your ViewModels are relying on DependencyObjects.

This is unfortunate, in my mind, because DependencyObjects are view objects being referenced by the ViewModel. I could ignore this if it were for purity alone. Unfortunately, I the problem I run into happens during testing with Silverlight.

When we are using NUnit to test your ViewModel in Silverlight, we get an exception as soon as we instantiate the BindingObject. This is because the base constructor for DependencyObject relies on the Silverlight runtime being instantiated... in a unit test, this is not the case.

That being said, I have also struggled with this myself. It turns out that the problem of binding is not terribly difficult. I just wrote my own binder. It works directly against any object that implements INotifyPropertyChanged. If both objects implement INotifyPropertyChanged, you get 2-way binding. I also can attach events to specific properties changing.

That approach works well, but it is a shame we have to re-invent the wheel. Ideally, WPF and Silverlight binding would not rely on DependencyProperties and therefore DependencyObjects. It is a shame that they don't. DependencyProperties are ugly as sin, in my humble opinion.

That being said, I have grown to prefer the EventAggregator in Prism for intra-ViewModel communication. Mostly because eventing tends to be a more proper analogy to what is happening between the VMs than binding, in my experience.

All in all, I love seeing clever ways of getting around binding limitations. It at least reduces your requirement that your ViewModel be a DependencyObject, which I really appreciate.

© 2005 - 2014 Josh Twist - All Rights Reserved.