From 7b9a6f29c98e1da7241e7a46213c8a7866dbca4e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 6 Mar 2016 16:01:25 +0100 Subject: [PATCH] Added GetWeakObservable(). And use in in bindings. --- .../Plugins/PerspexPropertyAccessorPlugin.cs | 2 +- src/Perspex.Base/PerspexObjectExtensions.cs | 17 ++++ .../PerspexPropertyChangedEventArgs.cs | 3 +- .../Reactive/PerspexObservable.cs | 2 +- .../Reactive/WeakPropertyChangedObservable.cs | 85 +++++++++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 src/Perspex.Base/Reactive/WeakPropertyChangedObservable.cs diff --git a/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs b/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs index b8e7cadfc1..61f05ce4f2 100644 --- a/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs +++ b/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs @@ -74,7 +74,7 @@ namespace Perspex.Markup.Data.Plugins _reference = reference; _property = property; - _subscription = Instance.GetObservable(property).Skip(1).Subscribe(changed); + _subscription = Instance.GetWeakObservable(property).Skip(1).Subscribe(changed); } public PerspexObject Instance diff --git a/src/Perspex.Base/PerspexObjectExtensions.cs b/src/Perspex.Base/PerspexObjectExtensions.cs index dc0a6b6465..fcb4e8dc32 100644 --- a/src/Perspex.Base/PerspexObjectExtensions.cs +++ b/src/Perspex.Base/PerspexObjectExtensions.cs @@ -164,6 +164,23 @@ namespace Perspex return result; } + /// + /// Gets a weak observable for a . + /// + /// The object. + /// The property. + /// An observable. + public static IObservable GetWeakObservable(this IPerspexObject o, PerspexProperty property) + { + Contract.Requires(o != null); + Contract.Requires(property != null); + + return new WeakPropertyChangedObservable( + new WeakReference(o), + property, + GetDescription(o, property)); + } + /// /// Binds a property on an to an . /// diff --git a/src/Perspex.Base/PerspexPropertyChangedEventArgs.cs b/src/Perspex.Base/PerspexPropertyChangedEventArgs.cs index 1136b5ce2a..4563278acc 100644 --- a/src/Perspex.Base/PerspexPropertyChangedEventArgs.cs +++ b/src/Perspex.Base/PerspexPropertyChangedEventArgs.cs @@ -1,6 +1,7 @@ // Copyright (c) The Perspex Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using Perspex.Data; namespace Perspex @@ -8,7 +9,7 @@ namespace Perspex /// /// Provides information for a perspex property change. /// - public class PerspexPropertyChangedEventArgs + public class PerspexPropertyChangedEventArgs : EventArgs { /// /// Initializes a new instance of the class. diff --git a/src/Perspex.Base/Reactive/PerspexObservable.cs b/src/Perspex.Base/Reactive/PerspexObservable.cs index bd76f544d4..a939bf33b5 100644 --- a/src/Perspex.Base/Reactive/PerspexObservable.cs +++ b/src/Perspex.Base/Reactive/PerspexObservable.cs @@ -11,7 +11,7 @@ namespace Perspex.Reactive /// An with an additional description. /// /// The type of the elements in the sequence. - public sealed class PerspexObservable : ObservableBase, IDescription + public class PerspexObservable : ObservableBase, IDescription { private readonly Func, IDisposable> _subscribe; diff --git a/src/Perspex.Base/Reactive/WeakPropertyChangedObservable.cs b/src/Perspex.Base/Reactive/WeakPropertyChangedObservable.cs new file mode 100644 index 0000000000..117fcc65ad --- /dev/null +++ b/src/Perspex.Base/Reactive/WeakPropertyChangedObservable.cs @@ -0,0 +1,85 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Reactive; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Perspex.Utilities; + +namespace Perspex.Reactive +{ + internal class WeakPropertyChangedObservable : ObservableBase, + IWeakSubscriber, IDescription + { + private WeakReference _sourceReference; + private readonly PerspexProperty _property; + private readonly Subject _changed = new Subject(); + + private int _count; + + public WeakPropertyChangedObservable( + WeakReference source, + PerspexProperty property, + string description) + { + _sourceReference = source; + _property = property; + Description = description; + } + + public string Description { get; } + + public void OnEvent(object sender, PerspexPropertyChangedEventArgs e) + { + if (e.Property == _property) + { + _changed.OnNext(e.NewValue); + } + } + + protected override IDisposable SubscribeCore(IObserver observer) + { + IPerspexObject instance; + + if (_sourceReference.TryGetTarget(out instance)) + { + if (_count == 0) + { + WeakSubscriptionManager.Subscribe( + instance, + nameof(instance.PropertyChanged), + this); + } + + observer.OnNext(instance.GetValue(_property)); + + return Observable.Using(() => Disposable.Create(DecrementCount), _ => _changed) + .Subscribe(observer); + } + else + { + _changed.OnCompleted(); + observer.OnCompleted(); + return Disposable.Empty; + } + } + + private void DecrementCount() + { + if (--_count == 0) + { + IPerspexObject instance; + + if (_sourceReference.TryGetTarget(out instance)) + { + WeakSubscriptionManager.Unsubscribe( + instance, + nameof(instance.PropertyChanged), + this); + } + } + } + } +}