none
Best way to make Dependency Properties into Observables?

    Question

  • Ive posted this previously on c9 ut i think i'll have better reach here :)
    ive been experimienting with converting DependencyProperties to Observables and this is what ive come up with:

      public class DependencyObservable<T> : IDisposable {
    EventHandler eh;
    DependencyPropertyDescriptor des;
    object instance;
    public DependencyObservable( IObserver<T> observer, DependencyProperty dp, Type ownertype, object ins ) {
    des = DependencyPropertyDescriptor.FromProperty( dp, ownertype );
    instance = ins;
    eh = new EventHandler( ( o, e ) => observer.OnNext( ( T )des.GetValue( instance ) ) );
    des.AddValueChanged( instance, eh );
    } public void Dispose( ) {
    des.RemoveValueChanged( instance, eh );
    }
    }

    public static class WindowExtensions {
    public static IObservable<double> HeightAsObservable( this MainWindow w ) {
    return Observable.CreateWithDisposable<double>( obs => new DependencyObservable<double>( obs, MainWindow.HeightProperty, w.GetType( ), w ) );
    }
    }

    and here is an example of using the extension method,

        public MainWindow( ) {
    InitializeComponent( );
    Observable.Context = SynchronizationContext.Current;
    this.HeightAsObservable( ).Subscribe( d => label1.Content = d );
    }

    is there anything i should do diffrent? should i call onCompleted in dispose for example? is there a way to do it more elegantly, perhaps without a helper class?
    ive tested this approach with binding and animation and it seems to work

    Thursday, November 19, 2009 2:34 PM

Answers

  • After watching Eriks excelent pdc talk i found that you can actually do this without a helper class, check out this extension method for the Height property:

        public static IObservable<double> HeightAsObservable( this MainWindow w ) {
          return Observable.Create<double>( o => {
            var des = DependencyPropertyDescriptor.FromProperty( MainWindow.HeightProperty, typeof( MainWindow ) );
            var eh = new EventHandler( ( s, e ) => o.OnNext( ( double )des.GetValue( w ) ) );
            des.AddValueChanged( w, eh );
            return ( ) => des.RemoveValueChanged( w, eh );
          } );
        }

    you could also generelize it to all Dependecy properties like this:

        public static IObservable<T> ToObservable<T>( this DependencyObject dependencyObject, DependencyProperty property ) {
          return Observable.Create<T>( o => {
            var des = DependencyPropertyDescriptor.FromProperty( property, dependencyObject.GetType( ) );
            var eh = new EventHandler( ( s, e ) => o.OnNext( ( T )des.GetValue( dependencyObject ) ) );
            des.AddValueChanged( dependencyObject, eh );
            return ( ) => des.RemoveValueChanged( dependencyObject, eh );
          } );
        }

    using the above extension method, you can do what vcomrhencke wants, modded from my original post:

    this.ToObservable<double>( MainWindow.HeightProperty )

    Alternativly, the method above can be an observable of object.

    i havent tested this with attatched propertis but in theory that should work :P
    it does work with regular DPs though and also works with animation and bindings

    any comments are welcome :)
    Tuesday, November 24, 2009 1:21 PM
  • You definitely don't butcher the API.  I would probably rewrite DependencyObservable as:

    IObservable<T> FromDependencyProperty<T>(object target, DependencyProperty property)
    {
       // argument checking
    
       return Observable.Create<T>(o =>
         {
            var d = DependencyPropertyDescriptor.FromProperty(property, target.GetType());
            var h = new EventHandler((o, e) => observer.OnNext((T)d.GetValue(target)));
            d.AddValueChanged(target, h);
            return () => d.RemoveValueChanged(target, h);
         });
    }


    Wednesday, November 25, 2009 12:23 AM
  • i did a little test and this approach also seems to work with attatched properties like this:

    rectangle.ToObservable<double>( Canvas.LeftProperty ).Subscribe( d => label2.Content = d );

    where rectangle is in a canvas and the Canvas.Left is bound to something (a slider that is two-way bound to the window height i my case)
    Tuesday, November 24, 2009 1:48 PM
  • Hi, 

    Please see my response in the following thread.  It works on dependency properties, INotifyPropertyChanged implementations, and also the [Property]Changed event pattern.

    http://social.msdn.microsoft.com/Forums/en-US/rx/thread/36bf6ecb-70ea-4be3-aa35-b9a9cbc9a078 
     
    - Dave
    http://davesexton.com/blog
    Saturday, January 02, 2010 7:51 AM

All replies

  • Hi Al,

    will have to play with this a bit before being able to see if I can propose any changes. For now, take a look at our IPropertyGetter/IPropertySetter interfaces in System.CoreEx and the various conversion to and from Obseravble/Observer of these interfaces in Observable and Observer.

    Jeffrey
    Thursday, November 19, 2009 3:47 PM
  • I would love to know this as well.  It would be nice if there was a method perhaps like:

    public static IObservable<IEvent<DependencyPropertyChangedEventArgs>> ToObservable(this DependencyObject object, DependencyProperty property)
    { ... }
    
    // or
    
    public static IObservable<IEvent<PropertyChangedEventArgs<T>>> ToObservable<T>(this DependencyObject object, DependencyProperty property)
    // where T is property.PropertyType
    { ... }
    
    which could be used like:
    // using System.Windows.Controls;
    
    var button = new Button();
    
    var buttonHeightChanged = button.ToObservable(Button.HeightProperty);
    
    // or
    
    var buttonHeightChanged = button.ToObservable<double>(Button.HeightProperty);
    Thoughts?
    Monday, November 23, 2009 5:19 PM
  • After watching Eriks excelent pdc talk i found that you can actually do this without a helper class, check out this extension method for the Height property:

        public static IObservable<double> HeightAsObservable( this MainWindow w ) {
          return Observable.Create<double>( o => {
            var des = DependencyPropertyDescriptor.FromProperty( MainWindow.HeightProperty, typeof( MainWindow ) );
            var eh = new EventHandler( ( s, e ) => o.OnNext( ( double )des.GetValue( w ) ) );
            des.AddValueChanged( w, eh );
            return ( ) => des.RemoveValueChanged( w, eh );
          } );
        }

    you could also generelize it to all Dependecy properties like this:

        public static IObservable<T> ToObservable<T>( this DependencyObject dependencyObject, DependencyProperty property ) {
          return Observable.Create<T>( o => {
            var des = DependencyPropertyDescriptor.FromProperty( property, dependencyObject.GetType( ) );
            var eh = new EventHandler( ( s, e ) => o.OnNext( ( T )des.GetValue( dependencyObject ) ) );
            des.AddValueChanged( dependencyObject, eh );
            return ( ) => des.RemoveValueChanged( dependencyObject, eh );
          } );
        }

    using the above extension method, you can do what vcomrhencke wants, modded from my original post:

    this.ToObservable<double>( MainWindow.HeightProperty )

    Alternativly, the method above can be an observable of object.

    i havent tested this with attatched propertis but in theory that should work :P
    it does work with regular DPs though and also works with animation and bindings

    any comments are welcome :)
    Tuesday, November 24, 2009 1:21 PM
  • i did a little test and this approach also seems to work with attatched properties like this:

    rectangle.ToObservable<double>( Canvas.LeftProperty ).Subscribe( d => label2.Content = d );

    where rectangle is in a canvas and the Canvas.Left is bound to something (a slider that is two-way bound to the window height i my case)
    Tuesday, November 24, 2009 1:48 PM
  • Oh, now that is quite awesome! :)

    That totally made my day.  Excellent work, aL!   :)
    Tuesday, November 24, 2009 2:11 PM
  • my pleasure :)
    id love to hear from someone on the team if they have any comments or if i butcher their api ;)

    Tuesday, November 24, 2009 2:30 PM
  • You definitely don't butcher the API.  I would probably rewrite DependencyObservable as:

    IObservable<T> FromDependencyProperty<T>(object target, DependencyProperty property)
    {
       // argument checking
    
       return Observable.Create<T>(o =>
         {
            var d = DependencyPropertyDescriptor.FromProperty(property, target.GetType());
            var h = new EventHandler((o, e) => observer.OnNext((T)d.GetValue(target)));
            d.AddValueChanged(target, h);
            return () => d.RemoveValueChanged(target, h);
         });
    }


    Wednesday, November 25, 2009 12:23 AM
  • cool, thanks :)
    why not an extension method though? and why on object and not dependecyobject?
    i think some version or other of this method would be great to have built into Rx, wpf is the future after all :)

    Wednesday, November 25, 2009 8:10 AM
  • Dependency object would be better you are right.  If it is dependency object then I think it could be an extension method.  It is generally in bad taste to add extension methods that apply to every object.
    Wednesday, November 25, 2009 6:55 PM
  • This would make an excellent addition to Rx.  It would be great if a future version had something like this. :)
    Wednesday, November 25, 2009 7:11 PM
  • Hi, 

    Please see my response in the following thread.  It works on dependency properties, INotifyPropertyChanged implementations, and also the [Property]Changed event pattern.

    http://social.msdn.microsoft.com/Forums/en-US/rx/thread/36bf6ecb-70ea-4be3-aa35-b9a9cbc9a078 
     
    - Dave
    http://davesexton.com/blog
    Saturday, January 02, 2010 7:51 AM