PageRenderTime 42ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/ReactiveUI.Xaml/DependencyObjectObservableForProperty.cs

https://github.com/bsiegel/ReactiveUI
C# | 108 lines | 82 code | 14 blank | 12 comment | 13 complexity | 493b63888377e16240c89216831637db MD5 | raw file
Possible License(s): Apache-2.0, CC-BY-SA-3.0, LGPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.Contracts;
  4. using System.Linq;
  5. using System.Reactive;
  6. using System.Reactive.Disposables;
  7. using System.Reactive.Linq;
  8. using System.Reactive.Subjects;
  9. using System.Reflection;
  10. using System.Text;
  11. #if WINRT
  12. using Windows.UI.Xaml;
  13. using Windows.UI.Xaml.Data;
  14. #else
  15. using System.Windows;
  16. using System.Windows.Data;
  17. #endif
  18. namespace ReactiveUI.Xaml
  19. {
  20. public class DependencyObjectObservableForProperty : ICreatesObservableForProperty
  21. {
  22. public int GetAffinityForObject(Type type, bool beforeChanged = false)
  23. {
  24. return typeof (DependencyObject).IsAssignableFrom(type) && beforeChanged == false ? 4 : 0;
  25. }
  26. static readonly Dictionary<Type, DependencyProperty> attachedProperties = new Dictionary<Type, DependencyProperty>();
  27. static readonly Dictionary<object, Tuple<Subject<object>, RefcountDisposeWrapper>> subjects = new Dictionary<object, Tuple<Subject<object>, RefcountDisposeWrapper>>();
  28. public IObservable<IObservedChange<object, object>> GetNotificationForProperty(object sender, string propertyName, bool beforeChanged = false)
  29. {
  30. Contract.Requires(sender != null && sender is DependencyObject);
  31. var dobj = sender as DependencyObject;
  32. var type = dobj.GetType();
  33. // Look for the DependencyProperty attached to this property name
  34. #if WINRT
  35. var pi = type.GetProperty(propertyName + "Property", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
  36. if (pi != null) {
  37. goto itWorks;
  38. }
  39. #endif
  40. var fi = type.GetField(propertyName + "Property", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
  41. if (fi == null) {
  42. this.Log().Debug("Tried to bind DO {0}.{1}, but DP doesn't exist. Binding as POCO object",
  43. type.FullName, propertyName);
  44. var ret = new POCOObservableForProperty();
  45. return ret.GetNotificationForProperty(sender, propertyName, beforeChanged);
  46. }
  47. itWorks:
  48. return Observable.Create<IObservedChange<object, object>>(subj => {
  49. DependencyProperty attachedProp;
  50. if (!attachedProperties.ContainsKey(type)) {
  51. // NB: There is no way to unregister an attached property,
  52. // we just have to leak it. Luckily it's per-type, so it's
  53. // not *that* bad.
  54. attachedProp = DependencyProperty.RegisterAttached(
  55. "ListenAttached" + propertyName + this.GetHashCode().ToString("{0:x}"),
  56. typeof(object), type,
  57. new PropertyMetadata(null, (o,e) => subjects[o].Item1.OnNext(o)));
  58. attachedProperties[type] = attachedProp;
  59. } else {
  60. attachedProp = attachedProperties[type];
  61. }
  62. // Here's the idea for this cracked-out code:
  63. //
  64. // The reason we're doing all of this is that we can only
  65. // create a single binding between a DependencyObject and its
  66. // attached property, yet we could have multiple people
  67. // interested in this property. We should only drop the actual
  68. // Binding once nobody is listening anymore.
  69. if (!subjects.ContainsKey(sender)) {
  70. var disposer = new RefcountDisposeWrapper(
  71. Disposable.Create(() => {
  72. #if !SILVERLIGHT && !WINRT
  73. // XXX: Apparently it's simply impossible to unset a binding in SL :-/
  74. BindingOperations.ClearBinding(dobj, attachedProp);
  75. #endif
  76. subjects.Remove(dobj);
  77. }));
  78. subjects[sender] = Tuple.Create(new Subject<object>(), disposer);
  79. var b = new Binding() { Source = dobj, Path = new PropertyPath(propertyName) };
  80. BindingOperations.SetBinding(dobj, attachedProp, b);
  81. } else {
  82. subjects[sender].Item2.AddRef();
  83. }
  84. var disp = subjects[sender].Item1
  85. .Select(x => (IObservedChange<object, object>) new ObservedChange<object, object>() { Sender = x, PropertyName = propertyName })
  86. .Subscribe(subj);
  87. return Disposable.Create(() => {
  88. disp.Dispose();
  89. subjects[sender].Item2.Release();
  90. });
  91. });
  92. }
  93. }
  94. }