// 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.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using Perspex.Reactive; namespace Perspex { /// /// Provides extension methods for and related classes. /// public static class PerspexObjectExtensions { /// /// Gets an observable for a . /// /// The object. /// The property. /// An observable. public static IObservable GetObservable(this IPerspexObject o, PerspexProperty property) { Contract.Requires(o != null); Contract.Requires(property != null); return new PerspexObservable( observer => { EventHandler handler = (s, e) => { if (e.Property == property) { observer.OnNext(e.NewValue); } }; observer.OnNext(o.GetValue(property)); o.PropertyChanged += handler; return Disposable.Create(() => { o.PropertyChanged -= handler; }); }, GetDescription(o, property)); } /// /// Gets an observable for a . /// /// The object. /// The property type. /// The property. /// An observable. public static IObservable GetObservable(this IPerspexObject o, PerspexProperty property) { Contract.Requires(o != null); Contract.Requires(property != null); return o.GetObservable((PerspexProperty)property).Cast(); } /// /// Gets an observable for a . /// /// The object. /// The type of the property. /// The property. /// /// An observable which when subscribed pushes the old and new values of the property each /// time it is changed. /// public static IObservable> GetObservableWithHistory( this IPerspexObject o, PerspexProperty property) { Contract.Requires(o != null); Contract.Requires(property != null); return new PerspexObservable>( observer => { EventHandler handler = (s, e) => { if (e.Property == property) { observer.OnNext(Tuple.Create((T)e.OldValue, (T)e.NewValue)); } }; o.PropertyChanged += handler; return Disposable.Create(() => { o.PropertyChanged -= handler; }); }, GetDescription(o, property)); } /// /// Binds a property to a subject according to a . /// /// The object. /// The property to bind. /// The binding source. /// The binding mode. /// The binding priority. /// public static IDisposable Bind( this IPerspexObject o, PerspexProperty property, ISubject source, BindingMode mode, BindingPriority priority = BindingPriority.LocalValue) { Contract.Requires(o != null); Contract.Requires(property != null); Contract.Requires(source != null); switch (mode) { case BindingMode.Default: case BindingMode.OneWay: return o.Bind(property, source, priority); case BindingMode.TwoWay: return new CompositeDisposable( o.Bind(property, source, priority), o.GetObservable(property).Subscribe(source)); case BindingMode.OneTime: return source.Take(1).Subscribe(x => o.SetValue(property, x, priority)); case BindingMode.OneWayToSource: return o.GetObservable(property).Subscribe(source); default: throw new ArgumentException("Invalid binding mode."); } } /// /// Subscribes to a property changed notifications for changes that originate from a /// . /// /// The type of the property change sender. /// The property changed observable. /// /// The method to call. The parameters are the sender and the event args. /// /// A disposable that can be used to terminate the subscription. public static IDisposable AddClassHandler( this IObservable observable, Action action) where TTarget : PerspexObject { return observable.Subscribe(e => { if (e.Sender is TTarget) { action((TTarget)e.Sender, e); } }); } /// /// Subscribes to a property changed notifications for changes that originate from a /// . /// /// The type of the property change sender. /// The property changed observable. /// Given a TTarget, returns the handler. /// A disposable that can be used to terminate the subscription. public static IDisposable AddClassHandler( this IObservable observable, Func> handler) where TTarget : class { return observable.Subscribe(e => SubscribeAdapter(e, handler)); } /// /// Gets a description of a property that van be used in observables. /// /// The object. /// The property /// The description. private static string GetDescription(IPerspexObject o, PerspexProperty property) { return $"{o.GetType().Name}.{property.Name}"; } /// /// Observer method for . /// /// The sender type to accept. /// The event args. /// Given a TTarget, returns the handler. private static void SubscribeAdapter( PerspexPropertyChangedEventArgs e, Func> handler) where TTarget : class { var target = e.Sender as TTarget; if (target != null) { handler(target)(e); } } } }