Browse Source

Added IPerspexObject.GetSubject extension method.

pull/396/head
Steven Kirk 10 years ago
parent
commit
36543720ed
  1. 81
      src/Perspex.Base/PerspexObjectExtensions.cs
  2. 49
      tests/Perspex.Base.UnitTests/PerspexObjectTests_GetSubject.cs

81
src/Perspex.Base/PerspexObjectExtensions.cs

@ -2,6 +2,7 @@
// 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;
@ -101,6 +102,37 @@ namespace Perspex
GetDescription(o, property));
}
/// <summary>
/// Gets a subject for a <see cref="PerspexProperty"/>.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="o">The object.</param>
/// <param name="property">The property.</param>
/// <param name="priority">
/// The priority with which binding values are written to the object.
/// </param>
/// <returns>
/// An <see cref="ISubject{T}"/> which can be used for two-way binding to/from the
/// property.
/// </returns>
public static ISubject<T> GetSubject<T>(
this IPerspexObject o,
PerspexProperty<T> property,
BindingPriority priority = BindingPriority.LocalValue)
{
// TODO: Subject.Create<T> is not yet in stable Rx : once it is, remove the
// AnonymousSubject classes from this file and use Subject.Create<T>.
var output = new Subject<T>();
var result = new AnonymousSubject<T>(
Observer.Create<T>(
x => output.OnNext(x),
e => output.OnError(e),
() => output.OnCompleted()),
o.GetObservable(property));
o.Bind(property, output, priority);
return result;
}
/// <summary>
/// Binds a property to a subject according to a <see cref="BindingMode"/>.
/// </summary>
@ -209,5 +241,54 @@ namespace Perspex
handler(target)(e);
}
}
class AnonymousSubject<T, U> : ISubject<T, U>
{
private readonly IObserver<T> _observer;
private readonly IObservable<U> _observable;
public AnonymousSubject(IObserver<T> observer, IObservable<U> observable)
{
_observer = observer;
_observable = observable;
}
public void OnCompleted()
{
_observer.OnCompleted();
}
public void OnError(Exception error)
{
if (error == null)
throw new ArgumentNullException("error");
_observer.OnError(error);
}
public void OnNext(T value)
{
_observer.OnNext(value);
}
public IDisposable Subscribe(IObserver<U> observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
//
// [OK] Use of unsafe Subscribe: non-pretentious wrapping of an observable sequence.
//
return _observable.Subscribe/*Unsafe*/(observer);
}
}
class AnonymousSubject<T> : AnonymousSubject<T, T>, ISubject<T>
{
public AnonymousSubject(IObserver<T> observer, IObservable<T> observable)
: base(observer, observable)
{
}
}
}
}

49
tests/Perspex.Base.UnitTests/PerspexObjectTests_GetSubject.cs

@ -0,0 +1,49 @@
// 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.Collections.Generic;
using System.Reactive.Linq;
using Xunit;
namespace Perspex.Base.UnitTests
{
public class PerspexObjectTests_GetSubject
{
[Fact]
public void GetSubject_Returns_Values()
{
var source = new Class1 { Foo = "foo" };
var target = source.GetSubject(Class1.FooProperty);
var result = new List<string>();
target.Subscribe(x => result.Add(x));
source.Foo = "bar";
source.Foo = "baz";
Assert.Equal(new[] { "foo", "bar", "baz" }, result);
}
[Fact]
public void GetSubject_Sets_Values()
{
var source = new Class1 { Foo = "foo" };
var target = source.GetSubject(Class1.FooProperty);
target.OnNext("bar");
Assert.Equal("bar", source.Foo);
}
private class Class1 : PerspexObject
{
public static readonly PerspexProperty<string> FooProperty =
PerspexProperty.Register<Class1, string>("Foo", "foodefault");
public string Foo
{
get { return GetValue(FooProperty); }
set { SetValue(FooProperty, value); }
}
}
}
}
Loading…
Cancel
Save