103 changed files with 1607 additions and 1311 deletions
@ -1,4 +1,9 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<packages> |
<packages> |
||||
|
<package id="Rx-Core" version="2.2.5" targetFramework="portable45-net45+win8" /> |
||||
|
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable45-net45+win8" /> |
||||
|
<package id="Rx-Linq" version="2.2.5" targetFramework="portable45-net45+win8" /> |
||||
|
<package id="Rx-Main" version="2.2.5" targetFramework="portable45-net45+win8" /> |
||||
|
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="portable45-net45+win8" /> |
||||
<package id="Splat" version="1.6.2" targetFramework="portable45-net45+win8" /> |
<package id="Splat" version="1.6.2" targetFramework="portable45-net45+win8" /> |
||||
</packages> |
</packages> |
||||
@ -1,35 +0,0 @@ |
|||||
// 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.Subjects; |
|
||||
|
|
||||
namespace Perspex.Markup.Xaml.Data |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Defines a binding that can be created in XAML markup.
|
|
||||
/// </summary>
|
|
||||
public interface IXamlBinding |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Applies the binding to a property on an instance.
|
|
||||
/// </summary>
|
|
||||
/// <param name="instance">The target instance.</param>
|
|
||||
/// <param name="property">The target property.</param>
|
|
||||
void Bind(IObservablePropertyBag instance, PerspexProperty property); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a subject that can be used to get and set the value of the binding.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">The target instance.</param>
|
|
||||
/// <param name="targetType">The type of the target property.</param>
|
|
||||
/// <param name="targetIsDataContext">
|
|
||||
/// Whether the target property is the DataContext property.
|
|
||||
/// </param>
|
|
||||
/// <returns>An <see cref="ISubject{object}"/>.</returns>
|
|
||||
ISubject<object> CreateSubject( |
|
||||
IObservablePropertyBag target, |
|
||||
Type targetType, |
|
||||
bool targetIsDataContext = false); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,20 @@ |
|||||
|
// 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; |
||||
|
|
||||
|
namespace Perspex.Data |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Signifies that a binding can be assigned to a property.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Usually in markup, when a binding is set for a property that property will be bound.
|
||||
|
/// Applying this attribute to a property indicates that the binding should be assigned to
|
||||
|
/// the property rather than bound.
|
||||
|
/// </remarks>
|
||||
|
[AttributeUsage(AttributeTargets.Property)] |
||||
|
public sealed class AssignBindingAttribute : Attribute |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
namespace Perspex.Data |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines possible binding modes.
|
||||
|
/// </summary>
|
||||
|
public enum BindingMode |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Uses the default binding mode specified for the property.
|
||||
|
/// </summary>
|
||||
|
Default, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Binds one way from source to target.
|
||||
|
/// </summary>
|
||||
|
OneWay, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Binds two-way with the initial value coming from the target.
|
||||
|
/// </summary>
|
||||
|
TwoWay, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Updates the target when the application starts or when the data context changes.
|
||||
|
/// </summary>
|
||||
|
OneTime, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Binds one way from target to source.
|
||||
|
/// </summary>
|
||||
|
OneWayToSource, |
||||
|
} |
||||
|
} |
||||
@ -1,7 +1,7 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
// 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.
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
namespace Perspex |
namespace Perspex.Data |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// The priority of a binding.
|
/// The priority of a binding.
|
||||
@ -0,0 +1,33 @@ |
|||||
|
// 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.Reactive.Subjects; |
||||
|
|
||||
|
namespace Perspex.Data |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Holds a binding that can be applied to a property on an object.
|
||||
|
/// </summary>
|
||||
|
public interface IBinding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the binding mode.
|
||||
|
/// </summary>
|
||||
|
BindingMode Mode { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the binding priority.
|
||||
|
/// </summary>
|
||||
|
BindingPriority Priority { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a subject that can be used to get and set the value of the binding.
|
||||
|
/// </summary>
|
||||
|
/// <param name="target">The target instance.</param>
|
||||
|
/// <param name="targetProperty">The target property. May be null.</param>
|
||||
|
/// <returns>An <see cref="ISubject{Object}"/>.</returns>
|
||||
|
ISubject<object> CreateSubject( |
||||
|
IPerspexObject target, |
||||
|
PerspexProperty targetProperty); |
||||
|
} |
||||
|
} |
||||
@ -1,95 +0,0 @@ |
|||||
// 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.Subjects; |
|
||||
|
|
||||
namespace Perspex |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Interface for getting/setting <see cref="PerspexProperty"/> bindings on an object.
|
|
||||
/// </summary>
|
|
||||
public interface IObservablePropertyBag : IPropertyBag |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Binds a <see cref="PerspexProperty"/> to an observable.
|
|
||||
/// </summary>
|
|
||||
/// <param name="property">The property.</param>
|
|
||||
/// <param name="source">The observable.</param>
|
|
||||
/// <param name="priority">The priority of the binding.</param>
|
|
||||
/// <returns>
|
|
||||
/// A disposable which can be used to terminate the binding.
|
|
||||
/// </returns>
|
|
||||
IDisposable Bind( |
|
||||
PerspexProperty property, |
|
||||
IObservable<object> source, |
|
||||
BindingPriority priority = BindingPriority.LocalValue); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Binds a <see cref="PerspexProperty"/> to an observable.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the property.</typeparam>
|
|
||||
/// <param name="property">The property.</param>
|
|
||||
/// <param name="source">The observable.</param>
|
|
||||
/// <param name="priority">The priority of the binding.</param>
|
|
||||
/// <returns>
|
|
||||
/// A disposable which can be used to terminate the binding.
|
|
||||
/// </returns>
|
|
||||
IDisposable Bind<T>( |
|
||||
PerspexProperty<T> property, |
|
||||
IObservable<T> source, |
|
||||
BindingPriority priority = BindingPriority.LocalValue); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initiates a two-way binding between <see cref="PerspexProperty"/>s.
|
|
||||
/// </summary>
|
|
||||
/// <param name="property">The property on this object.</param>
|
|
||||
/// <param name="source">The source object.</param>
|
|
||||
/// <param name="sourceProperty">The property on the source object.</param>
|
|
||||
/// <param name="priority">The priority of the binding.</param>
|
|
||||
/// <returns>
|
|
||||
/// A disposable which can be used to terminate the binding.
|
|
||||
/// </returns>
|
|
||||
/// <remarks>
|
|
||||
/// The binding is first carried out from <paramref name="source"/> to this.
|
|
||||
/// </remarks>
|
|
||||
IDisposable BindTwoWay( |
|
||||
PerspexProperty property, |
|
||||
PerspexObject source, |
|
||||
PerspexProperty sourceProperty, |
|
||||
BindingPriority priority = BindingPriority.LocalValue); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initiates a two-way binding between a <see cref="PerspexProperty"/> and an
|
|
||||
/// <see cref="ISubject{Object}"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="property">The property on this object.</param>
|
|
||||
/// <param name="source">The subject to bind to.</param>
|
|
||||
/// <param name="priority">The priority of the binding.</param>
|
|
||||
/// <returns>
|
|
||||
/// A disposable which can be used to terminate the binding.
|
|
||||
/// </returns>
|
|
||||
/// <remarks>
|
|
||||
/// The binding is first carried out from <paramref name="source"/> to this.
|
|
||||
/// </remarks>
|
|
||||
IDisposable BindTwoWay( |
|
||||
PerspexProperty property, |
|
||||
ISubject<object> source, |
|
||||
BindingPriority priority = BindingPriority.LocalValue); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an observable for a <see cref="PerspexProperty"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="property">The property.</param>
|
|
||||
/// <returns>An observable.</returns>
|
|
||||
IObservable<object> GetObservable(PerspexProperty property); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an observable for a <see cref="PerspexProperty"/>.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the property.</typeparam>
|
|
||||
/// <param name="property">The property.</param>
|
|
||||
/// <returns>An observable.</returns>
|
|
||||
IObservable<T> GetObservable<T>(PerspexProperty<T> property); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,16 @@ |
|||||
|
// 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.Subjects; |
||||
|
|
||||
|
namespace Perspex.Reactive |
||||
|
{ |
||||
|
public class AnonymousSubject<T> : AnonymousSubject<T, T>, ISubject<T> |
||||
|
{ |
||||
|
public AnonymousSubject(IObserver<T> observer, IObservable<T> observable) |
||||
|
: base(observer, observable) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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.Reactive.Subjects; |
||||
|
|
||||
|
namespace Perspex.Reactive |
||||
|
{ |
||||
|
public 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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
// 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.Linq; |
||||
|
|
||||
|
namespace Perspex.Styling |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// An observable which is switched on or off according to an activator observable.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// An <see cref="ActivatedObservable"/> has two inputs: an activator observable a
|
||||
|
/// <see cref="Source"/> observable which produces the activated value. When the activator
|
||||
|
/// produces true, the <see cref="ActivatedObservable"/> will produce the current activated
|
||||
|
/// value. When the activator produces false it will produce
|
||||
|
/// <see cref="PerspexProperty.UnsetValue"/>.
|
||||
|
/// </remarks>
|
||||
|
internal class ActivatedObservable : ObservableBase<object>, IDescription |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ActivatedObservable"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="activator">The activator.</param>
|
||||
|
/// <param name="source">An observable that produces the activated value.</param>
|
||||
|
/// <param name="description">The binding description.</param>
|
||||
|
public ActivatedObservable( |
||||
|
IObservable<bool> activator, |
||||
|
IObservable<object> source, |
||||
|
string description) |
||||
|
{ |
||||
|
Activator = activator; |
||||
|
Description = description; |
||||
|
Source = source; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the activator observable.
|
||||
|
/// </summary>
|
||||
|
public IObservable<bool> Activator { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a description of the binding.
|
||||
|
/// </summary>
|
||||
|
public string Description { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets an observable which produces the <see cref="ActivatedValue"/>.
|
||||
|
/// </summary>
|
||||
|
public IObservable<object> Source { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Notifies the provider that an observer is to receive notifications.
|
||||
|
/// </summary>
|
||||
|
/// <param name="observer">The observer.</param>
|
||||
|
/// <returns>IDisposable object used to unsubscribe from the observable sequence.</returns>
|
||||
|
protected override IDisposable SubscribeCore(IObserver<object> observer) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(observer != null); |
||||
|
|
||||
|
var sourceCompleted = Source.TakeLast(1).Select(_ => Unit.Default); |
||||
|
var activatorCompleted = Activator.TakeLast(1).Select(_ => Unit.Default); |
||||
|
var completed = sourceCompleted.Merge(activatorCompleted); |
||||
|
|
||||
|
return Activator |
||||
|
.CombineLatest(Source, (x, y) => new { Active = x, Value = y }) |
||||
|
.Select(x => x.Active ? x.Value : PerspexProperty.UnsetValue) |
||||
|
.DistinctUntilChanged() |
||||
|
.TakeUntil(completed) |
||||
|
.Subscribe(observer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
// 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.Linq; |
||||
|
using System.Reactive.Subjects; |
||||
|
|
||||
|
namespace Perspex.Styling |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A subject which is switched on or off according to an activator observable.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// An <see cref="ActivatedSubject"/> has two inputs: an activator observable and either an
|
||||
|
/// <see cref="ActivatedValue"/> or a <see cref="Source"/> observable which produces the
|
||||
|
/// activated value. When the activator produces true, the <see cref="ActivatedObservable"/> will
|
||||
|
/// produce the current activated value. When the activator produces false it will produce
|
||||
|
/// <see cref="PerspexProperty.UnsetValue"/>.
|
||||
|
/// </remarks>
|
||||
|
internal class ActivatedSubject : ActivatedObservable, ISubject<object>, IDescription |
||||
|
{ |
||||
|
private bool? _active; |
||||
|
private bool _completed; |
||||
|
private object _value; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ActivatedSubject"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="activator">The activator.</param>
|
||||
|
/// <param name="source">An observable that produces the activated value.</param>
|
||||
|
/// <param name="description">The binding description.</param>
|
||||
|
public ActivatedSubject( |
||||
|
IObservable<bool> activator, |
||||
|
ISubject<object> source, |
||||
|
string description) |
||||
|
: base(activator, source, description) |
||||
|
{ |
||||
|
Activator.Subscribe(ActivatorChanged, ActivatorError, ActivatorCompleted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the underlying subject.
|
||||
|
/// </summary>
|
||||
|
public new ISubject<object> Source |
||||
|
{ |
||||
|
get { return (ISubject<object>)base.Source; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Notifies all subscribed observers about the end of the sequence.
|
||||
|
/// </summary>
|
||||
|
public void OnCompleted() |
||||
|
{ |
||||
|
if (_active.Value && !_completed) |
||||
|
{ |
||||
|
Source.OnCompleted(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Notifies all subscribed observers with the exception.
|
||||
|
/// </summary>
|
||||
|
/// <param name="error">The exception to send to all subscribed observers.</param>
|
||||
|
/// <exception cref="ArgumentNullException"><paramref name="error"/> is null.</exception>
|
||||
|
public void OnError(Exception error) |
||||
|
{ |
||||
|
if (_active.Value && !_completed) |
||||
|
{ |
||||
|
Source.OnError(error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Notifies all subscribed observers with the value.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to send to all subscribed observers.</param>
|
||||
|
public void OnNext(object value) |
||||
|
{ |
||||
|
_value = value; |
||||
|
|
||||
|
if (_active.Value && !_completed) |
||||
|
{ |
||||
|
Source.OnNext(value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ActivatorChanged(bool active) |
||||
|
{ |
||||
|
bool first = !_active.HasValue; |
||||
|
|
||||
|
_active = active; |
||||
|
|
||||
|
if (!first) |
||||
|
{ |
||||
|
Source.OnNext(active ? _value : PerspexProperty.UnsetValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ActivatorCompleted() |
||||
|
{ |
||||
|
_completed = true; |
||||
|
Source.OnCompleted(); |
||||
|
} |
||||
|
|
||||
|
private void ActivatorError(Exception e) |
||||
|
{ |
||||
|
_completed = true; |
||||
|
Source.OnError(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
// 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.Linq; |
||||
|
|
||||
|
namespace Perspex.Styling |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// An value which is switched on or off according to an activator observable.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// An <see cref="ActivatedValue"/> has two inputs: an activator observable and an
|
||||
|
/// <see cref="Value"/>. When the activator produces true, the
|
||||
|
/// <see cref="ActivatedValue"/> will produce the current value. When the activator
|
||||
|
/// produces false it will produce <see cref="PerspexProperty.UnsetValue"/>.
|
||||
|
/// </remarks>
|
||||
|
internal class ActivatedValue : ObservableBase<object>, IDescription |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The activator.
|
||||
|
/// </summary>
|
||||
|
private readonly IObservable<bool> _activator; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ActivatedObservable"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="activator">The activator.</param>
|
||||
|
/// <param name="value">The activated value.</param>
|
||||
|
/// <param name="description">The binding description.</param>
|
||||
|
public ActivatedValue( |
||||
|
IObservable<bool> activator, |
||||
|
object value, |
||||
|
string description) |
||||
|
{ |
||||
|
_activator = activator; |
||||
|
Value = value; |
||||
|
Description = description; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the activated value.
|
||||
|
/// </summary>
|
||||
|
public object Value |
||||
|
{ |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a description of the binding.
|
||||
|
/// </summary>
|
||||
|
public string Description |
||||
|
{ |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Notifies the provider that an observer is to receive notifications.
|
||||
|
/// </summary>
|
||||
|
/// <param name="observer">The observer.</param>
|
||||
|
/// <returns>IDisposable object used to unsubscribe from the observable sequence.</returns>
|
||||
|
protected override IDisposable SubscribeCore(IObserver<object> observer) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(observer != null); |
||||
|
|
||||
|
return _activator |
||||
|
.Select(active => active ? Value : PerspexProperty.UnsetValue) |
||||
|
.Subscribe(observer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,65 +0,0 @@ |
|||||
// 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; |
|
||||
|
|
||||
namespace Perspex.Styling |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A setter for a <see cref="Style"/> whose source is an observable.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// A <see cref="Setter"/> is used to set a <see cref="PerspexProperty"/> value on a
|
|
||||
/// <see cref="PerspexObject"/> depending on a condition.
|
|
||||
/// </remarks>
|
|
||||
public class ObservableSetter : ISetter |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ObservableSetter"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="property">The property to set.</param>
|
|
||||
/// <param name="source">An observable which produces the value for the property.</param>
|
|
||||
public ObservableSetter(PerspexProperty property, IObservable<object> source) |
|
||||
{ |
|
||||
Property = property; |
|
||||
Source = source; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the property to set.
|
|
||||
/// </summary>
|
|
||||
public PerspexProperty Property |
|
||||
{ |
|
||||
get; |
|
||||
set; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets an observable which produces the value for the property.
|
|
||||
/// </summary>
|
|
||||
public IObservable<object> Source |
|
||||
{ |
|
||||
get; |
|
||||
set; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Applies the setter to the control.
|
|
||||
/// </summary>
|
|
||||
/// <param name="style">The style that is being applied.</param>
|
|
||||
/// <param name="control">The control.</param>
|
|
||||
/// <param name="activator">An optional activator.</param>
|
|
||||
public void Apply(IStyle style, IStyleable control, IObservable<bool> activator) |
|
||||
{ |
|
||||
if (activator == null) |
|
||||
{ |
|
||||
control.Bind(Property, Source, BindingPriority.Style); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
var binding = new StyleBinding(activator, Source, style.ToString()); |
|
||||
control.Bind(Property, binding, BindingPriority.StyleTrigger); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,107 +0,0 @@ |
|||||
// 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.Linq; |
|
||||
|
|
||||
namespace Perspex.Styling |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Provides an observable for a style.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// A <see cref="StyleBinding"/> has two inputs: an activator observable and either an
|
|
||||
/// <see cref="ActivatedValue"/> or a <see cref="Source"/> observable which produces the
|
|
||||
/// activated value. When the activator produces true, the <see cref="StyleBinding"/> will
|
|
||||
/// produce the current activated value. When the activator produces false it will produce
|
|
||||
/// <see cref="PerspexProperty.UnsetValue"/>.
|
|
||||
/// </remarks>
|
|
||||
internal class StyleBinding : ObservableBase<object>, IDescription |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The activator.
|
|
||||
/// </summary>
|
|
||||
private readonly IObservable<bool> _activator; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="StyleBinding"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="activator">The activator.</param>
|
|
||||
/// <param name="activatedValue">The activated value.</param>
|
|
||||
/// <param name="description">The binding description.</param>
|
|
||||
public StyleBinding( |
|
||||
IObservable<bool> activator, |
|
||||
object activatedValue, |
|
||||
string description) |
|
||||
{ |
|
||||
_activator = activator; |
|
||||
ActivatedValue = activatedValue; |
|
||||
Description = description; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="StyleBinding"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="activator">The activator.</param>
|
|
||||
/// <param name="source">An observable that produces the activated value.</param>
|
|
||||
/// <param name="description">The binding description.</param>
|
|
||||
public StyleBinding( |
|
||||
IObservable<bool> activator, |
|
||||
IObservable<object> source, |
|
||||
string description) |
|
||||
{ |
|
||||
_activator = activator; |
|
||||
Description = description; |
|
||||
Source = source; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the activated value.
|
|
||||
/// </summary>
|
|
||||
public object ActivatedValue |
|
||||
{ |
|
||||
get; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a description of the binding.
|
|
||||
/// </summary>
|
|
||||
public string Description |
|
||||
{ |
|
||||
get; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an observable which produces the <see cref="ActivatedValue"/>.
|
|
||||
/// </summary>
|
|
||||
public IObservable<object> Source |
|
||||
{ |
|
||||
get; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Notifies the provider that an observer is to receive notifications.
|
|
||||
/// </summary>
|
|
||||
/// <param name="observer">The observer.</param>
|
|
||||
/// <returns>IDisposable object used to unsubscribe from the observable sequence.</returns>
|
|
||||
protected override IDisposable SubscribeCore(IObserver<object> observer) |
|
||||
{ |
|
||||
Contract.Requires<ArgumentNullException>(observer != null); |
|
||||
|
|
||||
if (Source == null) |
|
||||
{ |
|
||||
return _activator |
|
||||
.Select(active => active ? ActivatedValue : PerspexProperty.UnsetValue) |
|
||||
.Subscribe(observer); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return _activator |
|
||||
.CombineLatest(Source, (x, y) => new { Active = x, Value = y }) |
|
||||
.Select(x => x.Active ? x.Value : PerspexProperty.UnsetValue) |
|
||||
.Subscribe(observer); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,120 @@ |
|||||
|
// 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.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using Moq; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Controls.Primitives; |
||||
|
using Perspex.Data; |
||||
|
using Perspex.Markup.Xaml.Data; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Styling; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.UnitTests |
||||
|
{ |
||||
|
public class StyleTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Binding_Should_Be_Assigned_To_Setter_Value_Instead_Of_Bound() |
||||
|
{ |
||||
|
using (PerspexLocator.EnterScope()) |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable |
||||
|
.Bind<IPclPlatformWrapper>() |
||||
|
.ToConstant(Mock.Of<IPclPlatformWrapper>()); |
||||
|
|
||||
|
var xaml = "<Style xmlns='https://github.com/perspex'><Setter Value='{Binding}'/></Style>"; |
||||
|
var loader = new PerspexXamlLoader(); |
||||
|
var style = (Style)loader.Load(xaml); |
||||
|
var setter = (Setter)(style.Setters.First()); |
||||
|
|
||||
|
Assert.IsType<Binding>(setter.Value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setter_With_TwoWay_Binding_Should_Update_Source() |
||||
|
{ |
||||
|
using (PerspexLocator.EnterScope()) |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable |
||||
|
.Bind<IPlatformThreadingInterface>() |
||||
|
.ToConstant(Mock.Of<IPlatformThreadingInterface>(x => |
||||
|
x.CurrentThreadIsLoopThread == true)); |
||||
|
|
||||
|
var data = new Data |
||||
|
{ |
||||
|
Foo = "foo", |
||||
|
}; |
||||
|
|
||||
|
var control = new TextBox |
||||
|
{ |
||||
|
DataContext = data, |
||||
|
}; |
||||
|
|
||||
|
var setter = new Setter |
||||
|
{ |
||||
|
Property = TextBox.TextProperty, |
||||
|
Value = new Binding |
||||
|
{ |
||||
|
Path = "Foo", |
||||
|
Mode = BindingMode.TwoWay |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
setter.Apply(null, control, null); |
||||
|
Assert.Equal("foo", control.Text); |
||||
|
|
||||
|
control.Text = "bar"; |
||||
|
Assert.Equal("bar", data.Foo); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setter_With_TwoWay_Binding_And_Activator_Should_Update_Source() |
||||
|
{ |
||||
|
using (PerspexLocator.EnterScope()) |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable |
||||
|
.Bind<IPlatformThreadingInterface>() |
||||
|
.ToConstant(Mock.Of<IPlatformThreadingInterface>(x => |
||||
|
x.CurrentThreadIsLoopThread == true)); |
||||
|
|
||||
|
var data = new Data |
||||
|
{ |
||||
|
Foo = "foo", |
||||
|
}; |
||||
|
|
||||
|
var control = new TextBox |
||||
|
{ |
||||
|
DataContext = data, |
||||
|
}; |
||||
|
|
||||
|
var setter = new Setter |
||||
|
{ |
||||
|
Property = TextBox.TextProperty, |
||||
|
Value = new Binding |
||||
|
{ |
||||
|
Path = "Foo", |
||||
|
Mode = BindingMode.TwoWay |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var activator = Observable.Never<bool>().StartWith(true); |
||||
|
|
||||
|
setter.Apply(null, control, activator); |
||||
|
Assert.Equal("foo", control.Text); |
||||
|
|
||||
|
control.Text = "bar"; |
||||
|
Assert.Equal("bar", data.Foo); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class Data |
||||
|
{ |
||||
|
public string Foo { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
// 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.Linq; |
||||
|
using Moq; |
||||
|
using Perspex.Controls.Templates; |
||||
|
using Perspex.Markup.Xaml.Data; |
||||
|
using Perspex.Markup.Xaml.Templates; |
||||
|
using Perspex.Platform; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.UnitTests |
||||
|
{ |
||||
|
public class TreeDataTemplateTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Binding_Should_Be_Assigned_To_ItemsSource_Instead_Of_Bound() |
||||
|
{ |
||||
|
using (PerspexLocator.EnterScope()) |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable |
||||
|
.Bind<IPclPlatformWrapper>() |
||||
|
.ToConstant(Mock.Of<IPclPlatformWrapper>()); |
||||
|
|
||||
|
var xaml = "<DataTemplates xmlns='https://github.com/perspex'><TreeDataTemplate ItemsSource='{Binding}'/></DataTemplates>"; |
||||
|
var loader = new PerspexXamlLoader(); |
||||
|
var templates = (DataTemplates)loader.Load(xaml); |
||||
|
var template = (TreeDataTemplate)(templates.First()); |
||||
|
|
||||
|
Assert.IsType<Binding>(template.ItemsSource); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
// 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 System.Reactive.Subjects; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Styling.UnitTests |
||||
|
{ |
||||
|
public class ActivatedObservableTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_Produce_Correct_Values() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var source = new BehaviorSubject<object>(1); |
||||
|
var target = new ActivatedObservable(activator, source, string.Empty); |
||||
|
var result = new List<object>(); |
||||
|
|
||||
|
target.Subscribe(x => result.Add(x)); |
||||
|
|
||||
|
activator.OnNext(true); |
||||
|
source.OnNext(2); |
||||
|
activator.OnNext(false); |
||||
|
source.OnNext(3); |
||||
|
activator.OnNext(true); |
||||
|
|
||||
|
Assert.Equal( |
||||
|
new[] |
||||
|
{ |
||||
|
PerspexProperty.UnsetValue, |
||||
|
1, |
||||
|
2, |
||||
|
PerspexProperty.UnsetValue, |
||||
|
3, |
||||
|
}, |
||||
|
result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Complete_When_Source_Completes() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var source = new BehaviorSubject<object>(1); |
||||
|
var target = new ActivatedObservable(activator, source, string.Empty); |
||||
|
var completed = false; |
||||
|
|
||||
|
target.Subscribe(_ => { }, () => completed = true); |
||||
|
source.OnCompleted(); |
||||
|
|
||||
|
Assert.True(completed); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Complete_When_Activator_Completes() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var source = new BehaviorSubject<object>(1); |
||||
|
var target = new ActivatedObservable(activator, source, string.Empty); |
||||
|
var completed = false; |
||||
|
|
||||
|
target.Subscribe(_ => { }, () => completed = true); |
||||
|
activator.OnCompleted(); |
||||
|
|
||||
|
Assert.True(completed); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,98 @@ |
|||||
|
// 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.Subjects; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Styling.UnitTests |
||||
|
{ |
||||
|
public class ActivatedSubjectTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_Set_Values() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var source = new TestSubject(); |
||||
|
var target = new ActivatedSubject(activator, source, string.Empty); |
||||
|
|
||||
|
target.OnNext("bar"); |
||||
|
Assert.Equal(PerspexProperty.UnsetValue, source.Value); |
||||
|
activator.OnNext(true); |
||||
|
target.OnNext("baz"); |
||||
|
Assert.Equal("baz", source.Value); |
||||
|
activator.OnNext(false); |
||||
|
Assert.Equal(PerspexProperty.UnsetValue, source.Value); |
||||
|
target.OnNext("bax"); |
||||
|
activator.OnNext(true); |
||||
|
Assert.Equal("bax", source.Value); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Invoke_OnCompleted_On_Activator_Completed() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var source = new TestSubject(); |
||||
|
var target = new ActivatedSubject(activator, source, string.Empty); |
||||
|
|
||||
|
activator.OnCompleted(); |
||||
|
|
||||
|
Assert.True(source.Completed); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Invoke_OnError_On_Activator_Error() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var source = new TestSubject(); |
||||
|
var target = new ActivatedSubject(activator, source, string.Empty); |
||||
|
|
||||
|
activator.OnError(new Exception()); |
||||
|
|
||||
|
Assert.NotNull(source.Error); |
||||
|
} |
||||
|
|
||||
|
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); } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class TestSubject : ISubject<object> |
||||
|
{ |
||||
|
private IObserver<object> _observer; |
||||
|
|
||||
|
public bool Completed { get; set; } |
||||
|
public Exception Error { get; set; } |
||||
|
public object Value { get; set; } = PerspexProperty.UnsetValue; |
||||
|
|
||||
|
public void OnCompleted() |
||||
|
{ |
||||
|
Completed = true; |
||||
|
} |
||||
|
|
||||
|
public void OnError(Exception error) |
||||
|
{ |
||||
|
Error = error; |
||||
|
} |
||||
|
|
||||
|
public void OnNext(object value) |
||||
|
{ |
||||
|
Value = value; |
||||
|
} |
||||
|
|
||||
|
public IDisposable Subscribe(IObserver<object> observer) |
||||
|
{ |
||||
|
_observer = observer; |
||||
|
return Disposable.Empty; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
// 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 System.Reactive.Subjects; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Styling.UnitTests |
||||
|
{ |
||||
|
public class ActivatedValueTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_Produce_Correct_Values() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var target = new ActivatedValue(activator, 1, string.Empty); |
||||
|
var result = new List<object>(); |
||||
|
|
||||
|
target.Subscribe(x => result.Add(x)); |
||||
|
|
||||
|
activator.OnNext(true); |
||||
|
activator.OnNext(false); |
||||
|
|
||||
|
Assert.Equal(new[] { PerspexProperty.UnsetValue, 1, PerspexProperty.UnsetValue }, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Complete_When_Activator_Completes() |
||||
|
{ |
||||
|
var activator = new BehaviorSubject<bool>(false); |
||||
|
var target = new ActivatedValue(activator, 1, string.Empty); |
||||
|
var completed = false; |
||||
|
|
||||
|
target.Subscribe(_ => { }, () => completed = true); |
||||
|
activator.OnCompleted(); |
||||
|
|
||||
|
Assert.True(completed); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
// 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.Reactive.Subjects; |
||||
|
using Moq; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Data; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Styling.UnitTests |
||||
|
{ |
||||
|
public class SetterTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Setter_Should_Apply_Binding_To_Property() |
||||
|
{ |
||||
|
var control = new TextBlock(); |
||||
|
var subject = new BehaviorSubject<object>("foo"); |
||||
|
var binding = Mock.Of<IBinding>(x => x.CreateSubject(control, TextBlock.TextProperty) == subject); |
||||
|
var style = Mock.Of<IStyle>(); |
||||
|
var setter = new Setter(TextBlock.TextProperty, binding); |
||||
|
|
||||
|
setter.Apply(style, control, null); |
||||
|
|
||||
|
Assert.Equal("foo", control.Text); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,79 +0,0 @@ |
|||||
// 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 System.Reactive.Subjects; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace Perspex.Styling.UnitTests |
|
||||
{ |
|
||||
public class StyleBindingTests |
|
||||
{ |
|
||||
[Fact] |
|
||||
public async void Should_Produce_UnsetValue_On_Activator_False() |
|
||||
{ |
|
||||
var activator = new BehaviorSubject<bool>(false); |
|
||||
var target = new StyleBinding(activator, 1, string.Empty); |
|
||||
var result = await target.Take(1); |
|
||||
|
|
||||
Assert.Equal(PerspexProperty.UnsetValue, result); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async void Should_Produce_Value_On_Activator_True() |
|
||||
{ |
|
||||
var activator = new BehaviorSubject<bool>(true); |
|
||||
var target = new StyleBinding(activator, 1, string.Empty); |
|
||||
var result = await target.Take(1); |
|
||||
|
|
||||
Assert.Equal(1, result); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_Change_Value_On_Activator_Change() |
|
||||
{ |
|
||||
var activator = new BehaviorSubject<bool>(false); |
|
||||
var target = new StyleBinding(activator, 1, string.Empty); |
|
||||
var result = new List<object>(); |
|
||||
|
|
||||
target.Subscribe(x => result.Add(x)); |
|
||||
|
|
||||
activator.OnNext(true); |
|
||||
activator.OnNext(false); |
|
||||
|
|
||||
Assert.Equal(new[] { PerspexProperty.UnsetValue, 1, PerspexProperty.UnsetValue }, result); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_Change_Value_With_Source_Observable() |
|
||||
{ |
|
||||
var activator = new BehaviorSubject<bool>(false); |
|
||||
var source = new BehaviorSubject<object>(1); |
|
||||
var target = new StyleBinding(activator, source, string.Empty); |
|
||||
var result = new List<object>(); |
|
||||
|
|
||||
target.Subscribe(x => result.Add(x)); |
|
||||
|
|
||||
activator.OnNext(true); |
|
||||
source.OnNext(2); |
|
||||
activator.OnNext(false); |
|
||||
|
|
||||
Assert.Equal(new[] { PerspexProperty.UnsetValue, 1, 2, PerspexProperty.UnsetValue }, result); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_Complete_When_Activator_Completes() |
|
||||
{ |
|
||||
var activator = new BehaviorSubject<bool>(false); |
|
||||
var target = new StyleBinding(activator, 1, string.Empty); |
|
||||
var completed = false; |
|
||||
|
|
||||
target.Subscribe(_ => { }, () => completed = true); |
|
||||
activator.OnCompleted(); |
|
||||
|
|
||||
Assert.True(completed); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue