Skip Navigation LinksHome > View Post

Bindorama - Binding Craziness now for Silverlight too!

A while back I posted And now for something completely crazy: Binding without WPF where I proposed a class that would allow you to use WPF's awesome binding deep inside your own code away from any visuals - awesome stuff for some complex ViewModel scenarios.

I unimaginatively entitled the class BindingObject but here's the Silverlight version and I've decided to name him: Bindorama!

Here's the code:

public class Bindorama : FrameworkElement, IDisposable
{
    private bool _suppress;
    private Action<DependencyPropertyChangedEventArgs> _onChanged;

    public Bindorama(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(Bindorama), new PropertyMetadata(null, ValueChangedCallback));

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

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

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

    private class Supresser : IDisposable
    {
            private Bindorama _bindingObject;

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

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

What are the advantages of this over just subscribing to INotifyPropertyChanged?

  • Weak reference based - no more leaking memory.
  • Can go deep on properties: "Property.That.I.Want.To.Monitor" - this is hard to do well manually
  • Can use indexers: "Collection[0]"
And here's how you use him:

Bindorama b = new Bindorama(objectYouWantToMonitor, "Property.You.Want.To.Monitor", DelegateToFireOnChange);

public void DelegateToFireOnChange(DependencyPropertyChangedEventArgs args)
{
    MessageBox.Show(string.Format("NewValue: {0}, OldValue: {1}",
        args.NewValue,
        args.OldValue);
}

Awesome!

Note, if the instance of Bindorama gets garbage collected then the event will stop firing (obviously). So you may need to keep a reference to it to keep it alive. Of course, this has huge advantages to help prevent memory leaks that so often occur when we start registering for events.

If you need to deterministically stop the events firing, then dispose the Bindorama (b.Dispose()). It's IDisposable so you can use it inside a using block if necessary.

Finally, I thought some extension methods might make this easier to use:

public static class BindoramaExtensions
{
    public static IDisposable Watch(this INotifyPropertyChanged source, string propertyExpression, Action<DependencyPropertyChangedEventArgs> onChanged)
    {
        return CoreWatch(source, propertyExpression, onChanged);
    }

    public static IDisposable Watch(this DependencyObject source, string propertyExpression, Action<DependencyPropertyChangedEventArgs> onChanged)
    {
        return CoreWatch(source, propertyExpression, onChanged);
    }

    private static IDisposable CoreWatch(object source, string propertyExpression, Action<DependencyPropertyChangedEventArgs> onChanged)
    {
        return new Bindorama(source, propertyExpression, onChanged);
    }
}

Which means you can now simply 'Watch' any INotifyPropertyChanged instance of DependencyObject easy peasy:

objectYouWantToMonitor.Watch("Property.You.Want.To.Monitor", args => MessageBox.Show(args.NewValue.ToString()));

Note that this returns IDisposable and he'll need to remain rooted if you want to receive change notifications (or dispose him to stop listening).

 
Josh Post By Josh Twist
12:21 AM
28 Jul 2009

» Next Post: New snippets for Silverlight and WPF
« Previous Post: Creating a Range Slider in Silverlight (and some of the binding gotchas in SL3)

Comments are closed for this post.

Posted by Stephen Cleary @ 28 Jul 2009 5:30 PM
I recently wrote something similar in intent, though very different in implementation; I finally wrote it up on my blog today: http://nitoprograms.blogspot.com/2009/07/this-post-illustrates-one-of-several.html

I'd be interested in hearing your thoughts.

© 2005 - 2014 Josh Twist - All Rights Reserved.