Note: Forums will be making significant UX changes to address key usability improvements surrounding search, discoverability and navigation. To learn more about these changes please visit the announcement which can be found HERE.
Best way to make Dependency Properties into Observables?

Answered Best way to make Dependency Properties into Observables?

  • Thursday, November 19, 2009 2:34 PM
     
     

    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

All Replies

  • Thursday, November 19, 2009 3:47 PM
     
     
    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
  • Monday, November 23, 2009 5:19 PM
     
      Has Code
    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?
  • Tuesday, November 24, 2009 1:21 PM
     
     Answered Has Code
    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 :)
    • Marked As Answer by Allan Lindqvist Wednesday, November 25, 2009 8:15 AM
    •  
  • Tuesday, November 24, 2009 1:48 PM
     
     Answered Has Code

    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)
    • Marked As Answer by Allan Lindqvist Wednesday, November 25, 2009 8:17 AM
    •  
  • Tuesday, November 24, 2009 2:11 PM
     
     
    Oh, now that is quite awesome! :)

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

  • Wednesday, November 25, 2009 12:23 AM
     
     Answered Has Code
    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);
         });
    }


    • Marked As Answer by Allan Lindqvist Wednesday, November 25, 2009 8:15 AM
    •  
  • Wednesday, November 25, 2009 8:10 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 6:55 PM
     
     
    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 7:11 PM
     
     
    This would make an excellent addition to Rx.  It would be great if a future version had something like this. :)
  • Saturday, January 02, 2010 7:51 AM
     
     Answered
    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