public IObservable <IObservedChange <object, object> > GetNotificationForProperty(object sender, string propertyName, bool beforeChanged = false) { Contract.Requires(sender != null && sender is DependencyObject); if (beforeChanged == true) { return(null); } var dobj = sender as DependencyObject; var type = dobj.GetType(); // Look for the DependencyProperty attached to this property name #if WINRT var pi = type.GetProperty(propertyName + "Property", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (pi != null) { goto itWorks; } #endif var fi = type.GetField(propertyName + "Property", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (fi == null) { this.Log().Debug("Tried to bind DO {0}.{1}, but DP doesn't exist. Binding as POCO object", type.FullName, propertyName); var ret = new POCOObservableForProperty(); return(ret.GetNotificationForProperty(sender, propertyName, beforeChanged)); } #if !WINRT && !SILVERLIGHT return(Observable.Create <IObservedChange <object, object> >(subj => { var dp = (DependencyProperty)fi.GetValue(null); var dpd = DependencyPropertyDescriptor.FromProperty(dp, type); var ev = new EventHandler((o, e) => subj.OnNext(new ObservedChange <object, object>() { Sender = sender, PropertyName = propertyName, })); dpd.AddValueChanged(sender, ev); return Disposable.Create(() => dpd.RemoveValueChanged(sender, ev)); })); #else itWorks: return(Observable.Create <IObservedChange <object, object> >(subj => { DependencyProperty attachedProp; if (!attachedProperties.ContainsKey(type)) { // NB: There is no way to unregister an attached property, // we just have to leak it. Luckily it's per-type, so it's // not *that* bad. attachedProp = DependencyProperty.RegisterAttached( "ListenAttached" + propertyName + this.GetHashCode().ToString("{0:x}"), typeof(object), type, new PropertyMetadata(null, (o, e) => subjects[o].Item1.OnNext(o))); attachedProperties[type] = attachedProp; } else { attachedProp = attachedProperties[type]; } // Here's the idea for this cracked-out code: // // The reason we're doing all of this is that we can only // create a single binding between a DependencyObject and its // attached property, yet we could have multiple people // interested in this property. We should only drop the actual // Binding once nobody is listening anymore. if (!subjects.ContainsKey(sender)) { var disposer = new RefcountDisposeWrapper( Disposable.Create(() => { #if !SILVERLIGHT && !WINRT // XXX: Apparently it's simply impossible to unset a binding in SL :-/ BindingOperations.ClearBinding(dobj, attachedProp); #endif subjects.Remove(dobj); })); subjects[sender] = Tuple.Create(new Subject <object>(), disposer); var b = new Binding() { Source = dobj, Path = new PropertyPath(propertyName) }; BindingOperations.SetBinding(dobj, attachedProp, b); } else { subjects[sender].Item2.AddRef(); } var disp = subjects[sender].Item1 .Select(x => (IObservedChange <object, object>) new ObservedChange <object, object>() { Sender = x, PropertyName = propertyName }) .Subscribe(subj); return Disposable.Create(() => { disp.Dispose(); subjects[sender].Item2.Release(); }); })); #endif }
public IObservable<IObservedChange<object, object>> GetNotificationForProperty(object sender, string propertyName, bool beforeChanged = false) { Contract.Requires(sender != null && sender is DependencyObject); var dobj = sender as DependencyObject; var type = dobj.GetType(); // Look for the DependencyProperty attached to this property name #if WINRT var pi = type.GetProperty(propertyName + "Property", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (pi != null) { goto itWorks; } #endif var fi = type.GetField(propertyName + "Property", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (fi == null) { this.Log().Debug("Tried to bind DO {0}.{1}, but DP doesn't exist. Binding as POCO object", type.FullName, propertyName); var ret = new POCOObservableForProperty(); return ret.GetNotificationForProperty(sender, propertyName, beforeChanged); } itWorks: return Observable.Create<IObservedChange<object, object>>(subj => { DependencyProperty attachedProp; if (!attachedProperties.ContainsKey(type)) { // NB: There is no way to unregister an attached property, // we just have to leak it. Luckily it's per-type, so it's // not *that* bad. attachedProp = DependencyProperty.RegisterAttached( "ListenAttached" + propertyName + this.GetHashCode().ToString("{0:x}"), typeof(object), type, new PropertyMetadata(null, (o,e) => subjects[o].Item1.OnNext(o))); attachedProperties[type] = attachedProp; } else { attachedProp = attachedProperties[type]; } // Here's the idea for this cracked-out code: // // The reason we're doing all of this is that we can only // create a single binding between a DependencyObject and its // attached property, yet we could have multiple people // interested in this property. We should only drop the actual // Binding once nobody is listening anymore. if (!subjects.ContainsKey(sender)) { var disposer = new RefcountDisposeWrapper( Disposable.Create(() => { #if !SILVERLIGHT && !WINRT // XXX: Apparently it's simply impossible to unset a binding in SL :-/ BindingOperations.ClearBinding(dobj, attachedProp); #endif subjects.Remove(dobj); })); subjects[sender] = Tuple.Create(new Subject<object>(), disposer); var b = new Binding() { Source = dobj, Path = new PropertyPath(propertyName) }; BindingOperations.SetBinding(dobj, attachedProp, b); } else { subjects[sender].Item2.AddRef(); } var disp = subjects[sender].Item1 .Select(x => (IObservedChange<object, object>) new ObservedChange<object, object>() { Sender = x, PropertyName = propertyName }) .Subscribe(subj); return Disposable.Create(() => { disp.Dispose(); subjects[sender].Item2.Release(); }); }); }