19 changed files with 1225 additions and 324 deletions
@ -0,0 +1,201 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="PriorityValueTests.cs" company="Steven Kirk">
|
|||
// Copyright 2013 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.UnitTests |
|||
{ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reactive.Disposables; |
|||
using System.Reactive.Linq; |
|||
using System.Reactive.Subjects; |
|||
using Microsoft.VisualStudio.TestTools.UnitTesting; |
|||
|
|||
[TestClass] |
|||
public class PriorityValueTests |
|||
{ |
|||
[TestMethod] |
|||
public void Initial_Value_Should_Be_UnsetValue() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
|
|||
Assert.AreSame(PerspexProperty.UnsetValue, target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void First_Binding_Sets_Value() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
|
|||
target.Add(this.Single("foo"), 0); |
|||
|
|||
Assert.AreEqual("foo", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Changing_Binding_Should_Set_Value() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
var subject = new BehaviorSubject<string>("foo"); |
|||
|
|||
target.Add(subject, 0); |
|||
Assert.AreEqual("foo", target.Value); |
|||
subject.OnNext("bar"); |
|||
Assert.AreEqual("bar", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Binding_With_Lower_Priority_Has_Precedence() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
|
|||
target.Add(this.Single("foo"), 1); |
|||
target.Add(this.Single("bar"), 0); |
|||
target.Add(this.Single("baz"), 1); |
|||
|
|||
Assert.AreEqual("bar", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Later_Binding_With_Same_Priority_Should_Take_Precedence() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
|
|||
target.Add(this.Single("foo"), 1); |
|||
target.Add(this.Single("bar"), 0); |
|||
target.Add(this.Single("baz"), 0); |
|||
target.Add(this.Single("qux"), 1); |
|||
|
|||
Assert.AreEqual("baz", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Changing_Binding_With_Lower_Priority_Should_Set_Not_Value() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
var subject = new BehaviorSubject<string>("bar"); |
|||
|
|||
target.Add(this.Single("foo"), 0); |
|||
target.Add(subject, 1); |
|||
Assert.AreEqual("foo", target.Value); |
|||
subject.OnNext("baz"); |
|||
Assert.AreEqual("foo", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void UnsetValue_Should_Fall_Back_To_Next_Binding() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
var subject = new BehaviorSubject<object>("bar"); |
|||
|
|||
target.Add(subject, 0); |
|||
target.Add(this.Single("foo"), 1); |
|||
|
|||
Assert.AreEqual("bar", target.Value); |
|||
|
|||
subject.OnNext(PerspexProperty.UnsetValue); |
|||
|
|||
Assert.AreEqual("foo", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Adding_Value_Should_Call_OnNext() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
bool called = false; |
|||
|
|||
target.Subscribe(value => called = (value.Item1 == PerspexProperty.UnsetValue && (string)value.Item2 == "foo")); |
|||
target.Add(this.Single("foo"), 0); |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Changing_Value_Should_Call_OnNext() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
var subject = new BehaviorSubject<object>("foo"); |
|||
bool called = false; |
|||
|
|||
target.Add(subject, 0); |
|||
target.Subscribe(value => called = ((string)value.Item1 == "foo" && (string)value.Item2 == "bar")); |
|||
subject.OnNext("bar"); |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Disposing_A_Binding_Should_Revert_To_Next_Value() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
|
|||
target.Add(this.Single("foo"), 0); |
|||
var disposable = target.Add(this.Single("bar"), 0); |
|||
|
|||
Assert.AreEqual("bar", target.Value); |
|||
disposable.Dispose(); |
|||
Assert.AreEqual("foo", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Disposing_A_Binding_Should_Remove_BindingEntry() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
|
|||
target.Add(this.Single("foo"), 0); |
|||
var disposable = target.Add(this.Single("bar"), 0); |
|||
|
|||
Assert.AreEqual(2, target.GetBindings().Count()); |
|||
disposable.Dispose(); |
|||
Assert.AreEqual(1, target.GetBindings().Count()); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Completing_A_Binding_Should_Revert_To_Next_Value() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
var subject = new BehaviorSubject<object>("bar"); |
|||
|
|||
target.Add(this.Single("foo"), 0); |
|||
target.Add(subject, 0); |
|||
|
|||
Assert.AreEqual("bar", target.Value); |
|||
subject.OnCompleted(); |
|||
Assert.AreEqual("foo", target.Value); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Completing_A_Binding_Should_Remove_BindingEntry() |
|||
{ |
|||
var target = new PriorityValue(); |
|||
var subject = new BehaviorSubject<object>("bar"); |
|||
|
|||
target.Add(this.Single("foo"), 0); |
|||
target.Add(subject, 0); |
|||
|
|||
Assert.AreEqual(2, target.GetBindings().Count()); |
|||
subject.OnCompleted(); |
|||
Assert.AreEqual(1, target.GetBindings().Count()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an observable that returns a single value but does not complete.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the observable.</typeparam>
|
|||
/// <param name="value">The value.</param>
|
|||
/// <returns>The observable.</returns>
|
|||
/// <remarks>
|
|||
/// Seems like there should be something that does this in Rx but I couldn't find it.
|
|||
/// </remarks>
|
|||
private IObservable<T> Single<T>(T value) |
|||
{ |
|||
return Observable.Create<T>(observer => |
|||
{ |
|||
observer.OnNext(value); |
|||
return Disposable.Create(() => { }); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="ActivatedBinding.cs" company="Tricycle">
|
|||
// Copyright 2013 Tricycle. All rights reserved.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Styling |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reactive.Disposables; |
|||
using System.Reactive.Subjects; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
internal class StyleBinding : IObservable<object>, IObservableDescription |
|||
{ |
|||
private List<IObserver<object>> observers = new List<IObserver<object>>(); |
|||
|
|||
public StyleBinding( |
|||
StyleActivator activator, |
|||
object activatedValue, |
|||
string description) |
|||
{ |
|||
this.Activator = activator; |
|||
this.ActivatedValue = activatedValue; |
|||
this.Description = description; |
|||
|
|||
this.Activator.Subscribe( |
|||
active => this.OnNext(active ? this.ActivatedValue : PerspexProperty.UnsetValue), |
|||
error => this.OnError(error), |
|||
() => this.OnCompleted()); |
|||
} |
|||
|
|||
public StyleActivator Activator |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public string Description |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public object ActivatedValue |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public IDisposable Subscribe(IObserver<object> observer) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(observer != null); |
|||
this.observers.Add(observer); |
|||
return Disposable.Create(() => this.observers.Remove(observer)); |
|||
} |
|||
|
|||
private void OnCompleted() |
|||
{ |
|||
foreach (var observer in this.observers) |
|||
{ |
|||
observer.OnCompleted(); |
|||
} |
|||
} |
|||
|
|||
private void OnError(Exception error) |
|||
{ |
|||
foreach (var observer in this.observers) |
|||
{ |
|||
observer.OnError(error); |
|||
} |
|||
} |
|||
|
|||
private void OnNext(object value) |
|||
{ |
|||
foreach (var observer in this.observers) |
|||
{ |
|||
observer.OnNext(value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,555 @@ |
|||
// ----------------------------------------------------------------------- |
|||
// <copyright file="PerspexObject.cs" company="Tricycle"> |
|||
// Copyright 2013 Tricycle. All rights reserved. |
|||
// </copyright> |
|||
// ----------------------------------------------------------------------- |
|||
|
|||
namespace Perspex |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
using System.Reactive; |
|||
using System.Reactive.Disposables; |
|||
using System.Reactive.Linq; |
|||
using System.Reflection; |
|||
using Splat; |
|||
|
|||
/// <summary> |
|||
/// The priority of a binding. |
|||
/// </summary> |
|||
public enum BindingPriority |
|||
{ |
|||
/// <summary> |
|||
/// A local value. |
|||
/// </summary> |
|||
LocalValue, |
|||
|
|||
/// <summary> |
|||
/// A style binding. |
|||
/// </summary> |
|||
Style, |
|||
|
|||
/// <summary> |
|||
/// A template binding. |
|||
/// </summary> |
|||
Template, |
|||
} |
|||
|
|||
/// <summary> |
|||
/// An object with <see cref="PerspexProperty"/> support. |
|||
/// </summary> |
|||
/// <remarks> |
|||
/// This class is analogous to DependencyObject in WPF. |
|||
/// </remarks> |
|||
public class PerspexObject : IEnableLogger |
|||
{ |
|||
/// <summary> |
|||
/// The registered properties by type. |
|||
/// </summary> |
|||
private static Dictionary<Type, List<PerspexProperty>> registered = |
|||
new Dictionary<Type, List<PerspexProperty>>(); |
|||
|
|||
/// <summary> |
|||
/// The parent object that inherited values are inherited from. |
|||
/// </summary> |
|||
private PerspexObject inheritanceParent; |
|||
|
|||
/// <summary> |
|||
/// The set values/bindings on this object. |
|||
/// </summary> |
|||
private Dictionary<PerspexProperty, PriorityValue> values = |
|||
new Dictionary<PerspexProperty, PriorityValue>(); |
|||
|
|||
/// <summary> |
|||
/// Raised when a <see cref="PerspexProperty"/> value changes on this object/ |
|||
/// </summary> |
|||
public event EventHandler<PerspexPropertyChangedEventArgs> PropertyChanged; |
|||
|
|||
/// <summary> |
|||
/// Gets or sets the parent object that inherited <see cref="PerspexProperty"/> values |
|||
/// are inherited from. |
|||
/// </summary> |
|||
protected PerspexObject InheritanceParent |
|||
{ |
|||
get |
|||
{ |
|||
return this.inheritanceParent; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
if (this.inheritanceParent != value) |
|||
{ |
|||
if (this.inheritanceParent != null) |
|||
{ |
|||
this.inheritanceParent.PropertyChanged -= this.ParentPropertyChanged; |
|||
} |
|||
|
|||
var inherited = (from property in GetProperties(this.GetType()) |
|||
where property.Inherits |
|||
select new |
|||
{ |
|||
Property = property, |
|||
Value = this.GetValue(property), |
|||
}).ToList(); |
|||
|
|||
this.inheritanceParent = value; |
|||
|
|||
foreach (var i in inherited) |
|||
{ |
|||
object newValue = this.GetValue(i.Property); |
|||
|
|||
if (!object.Equals(i.Value, newValue)) |
|||
{ |
|||
this.RaisePropertyChanged(i.Property, i.Value, newValue); |
|||
} |
|||
} |
|||
|
|||
if (this.inheritanceParent != null) |
|||
{ |
|||
this.inheritanceParent.PropertyChanged += this.ParentPropertyChanged; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Defers property change notifications until <see cref="EndDeferChanges"/> is called. |
|||
/// </summary> |
|||
public void BeginDeferChanges() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
//foreach (PriorityValue v in this.values.Values) |
|||
//{ |
|||
// v.BeginDeferChanges(); |
|||
//} |
|||
|
|||
//this.Log().Debug(string.Format( |
|||
// "Defer style changes on {0} (#{1:x8})", |
|||
// this.GetType().Name, |
|||
// this.GetHashCode())); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Ends the defer of property change notifications initiated by a previous call |
|||
/// to <see cref="BeginDeferChanges"/>. |
|||
/// </summary> |
|||
public void EndDeferChanges() |
|||
{ |
|||
//foreach (PriorityValue v in this.values.Values) |
|||
//{ |
|||
// v.EndDeferChanges(); |
|||
//} |
|||
|
|||
//this.Log().Debug(string.Format( |
|||
// "End defer style changes on {0} (#{1:x8})", |
|||
// this.GetType().Name, |
|||
// this.GetHashCode())); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets all <see cref="PerspexProperty"/>s registered on a type. |
|||
/// </summary> |
|||
/// <param name="type">The type.</param> |
|||
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns> |
|||
public static IEnumerable<PerspexProperty> GetProperties(Type type) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(type != null); |
|||
|
|||
TypeInfo i = type.GetTypeInfo(); |
|||
|
|||
while (type != null) |
|||
{ |
|||
List<PerspexProperty> list; |
|||
|
|||
if (registered.TryGetValue(type, out list)) |
|||
{ |
|||
foreach (PerspexProperty p in list) |
|||
{ |
|||
yield return p; |
|||
} |
|||
} |
|||
|
|||
type = type.GetTypeInfo().BaseType; |
|||
} |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Registers a <see cref="PerspexProperty"/> on a type. |
|||
/// </summary> |
|||
/// <param name="type">The type.</param> |
|||
/// <param name="property">The property.</param> |
|||
/// <remarks> |
|||
/// You won't usually want to call this method directly, instead use the |
|||
/// <see cref="PerspexProperty.Register"/> method. |
|||
/// </remarks> |
|||
public static void Register(Type type, PerspexProperty property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(type != null); |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
List<PerspexProperty> list; |
|||
|
|||
if (!registered.TryGetValue(type, out list)) |
|||
{ |
|||
list = new List<PerspexProperty>(); |
|||
registered.Add(type, list); |
|||
} |
|||
|
|||
if (!list.Contains(property)) |
|||
{ |
|||
list.Add(property); |
|||
} |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Clears a binding on a <see cref="PerspexProperty"/>, leaving the last bound value in |
|||
/// place. |
|||
/// </summary> |
|||
/// <param name="property">The property.</param> |
|||
public void ClearBinding(PerspexProperty property) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
//Contract.Requires<NullReferenceException>(property != null); |
|||
//PriorityValue value; |
|||
|
|||
//if (this.values.TryGetValue(property, out value)) |
|||
//{ |
|||
// value.ClearLocalBinding(); |
|||
|
|||
// this.Log().Debug(string.Format( |
|||
// "Cleared binding on {0}.{1} (#{2:x8})", |
|||
// this.GetType().Name, |
|||
// property.Name, |
|||
// this.GetHashCode())); |
|||
//} |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Clears a <see cref="PerspexProperty"/> value, including its binding. |
|||
/// </summary> |
|||
/// <param name="property">The property.</param> |
|||
public void ClearValue(PerspexProperty property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
this.SetValue(property, PerspexProperty.UnsetValue); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets an observable for a <see cref="PerspexProperty"/>. |
|||
/// </summary> |
|||
/// <param name="property"></param> |
|||
/// <returns></returns> |
|||
public IObservable<object> GetObservable(PerspexProperty property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
return Observable.Create<object>(observer => |
|||
{ |
|||
EventHandler<PerspexPropertyChangedEventArgs> handler = (s, e) => |
|||
{ |
|||
if (e.Property == property) |
|||
{ |
|||
observer.OnNext(e.NewValue); |
|||
} |
|||
}; |
|||
|
|||
this.PropertyChanged += handler; |
|||
observer.OnNext(this.GetValue(property)); |
|||
|
|||
return () => |
|||
{ |
|||
this.PropertyChanged -= handler; |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets an observable for a <see cref="PerspexProperty"/>. |
|||
/// </summary> |
|||
/// <typeparam name="T"></typeparam> |
|||
/// <param name="property"></param> |
|||
/// <returns></returns> |
|||
public IObservable<T> GetObservable<T>(PerspexProperty<T> property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
return this.GetObservable((PerspexProperty)property).Cast<T>(); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets an observable for a <see cref="ReadOnlyPerspexProperty"/>. |
|||
/// </summary> |
|||
/// <typeparam name="T"></typeparam> |
|||
/// <param name="property"></param> |
|||
/// <returns></returns> |
|||
public IObservable<T> GetObservable<T>(ReadOnlyPerspexProperty<T> property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
return this.GetObservable((PerspexProperty<T>)property.Property); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets an observable for a <see cref="PerspexProperty"/>. |
|||
/// </summary> |
|||
/// <typeparam name="T"></typeparam> |
|||
/// <param name="property"></param> |
|||
/// <returns></returns> |
|||
public IObservable<Tuple<T, T>> GetObservableWithHistory<T>(PerspexProperty<T> property) |
|||
{ |
|||
return Observable.Create<Tuple<T, T>>(observer => |
|||
{ |
|||
EventHandler<PerspexPropertyChangedEventArgs> handler = (s, e) => |
|||
{ |
|||
if (e.Property == property) |
|||
{ |
|||
observer.OnNext(Tuple.Create((T)e.OldValue, (T)e.NewValue)); |
|||
} |
|||
}; |
|||
|
|||
this.PropertyChanged += handler; |
|||
|
|||
return () => |
|||
{ |
|||
this.PropertyChanged -= handler; |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets a <see cref="PerspexProperty"/> value. |
|||
/// </summary> |
|||
/// <param name="property">The property.</param> |
|||
/// <returns>The value.</returns> |
|||
public object GetValue(PerspexProperty property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
object result; |
|||
|
|||
PriorityValue value; |
|||
|
|||
if (this.values.TryGetValue(property, out value)) |
|||
{ |
|||
result = value.Value; |
|||
} |
|||
else |
|||
{ |
|||
result = PerspexProperty.UnsetValue; |
|||
} |
|||
|
|||
if (result == PerspexProperty.UnsetValue) |
|||
{ |
|||
result = this.GetDefaultValue(property); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets a <see cref="PerspexProperty"/> value. |
|||
/// </summary> |
|||
/// <param name="property">The property.</param> |
|||
/// <returns>The value.</returns> |
|||
public T GetValue<T>(PerspexProperty<T> property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
return (T)this.GetValue((PerspexProperty)property); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Gets a <see cref="ReadOnlyPerspexProperty"/> value. |
|||
/// </summary> |
|||
/// <typeparam name="T">The type of the property.</typeparam> |
|||
/// <param name="property">The property.</param> |
|||
/// <returns>The value.</returns> |
|||
public T GetValue<T>(ReadOnlyPerspexProperty<T> property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
return (T)this.GetValue(property.Property); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Checks whether a <see cref="PerspexProperty"/> is set on this object. |
|||
/// </summary> |
|||
/// <param name="property"></param> |
|||
/// <returns></returns> |
|||
public bool IsSet(PerspexProperty property) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
return this.values.ContainsKey(property); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Sets a <see cref="PerspexProperty"/> value. |
|||
/// </summary> |
|||
/// <param name="property">The property.</param> |
|||
/// <param name="value">The value.</param> |
|||
public void SetValue(PerspexProperty property, object value) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
PriorityValue v; |
|||
|
|||
if (!this.values.TryGetValue(property, out v)) |
|||
{ |
|||
if (value == PerspexProperty.UnsetValue) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
v = this.CreatePriorityValue(property); |
|||
this.values.Add(property, v); |
|||
} |
|||
|
|||
throw new NotImplementedException(); |
|||
//v.SetLocalValue(value); |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Sets a <see cref="PerspexProperty"/> value. |
|||
/// </summary> |
|||
/// <typeparam name="T">The type of the property.</typeparam> |
|||
/// <param name="property">The property.</param> |
|||
/// <param name="value">The value.</param> |
|||
public void SetValue<T>(PerspexProperty<T> property, T value) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
this.SetValue((PerspexProperty)property, value); |
|||
} |
|||
|
|||
/// <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> |
|||
public void Bind( |
|||
PerspexProperty property, |
|||
IObservable<object> source, |
|||
BindingPriority priority = BindingPriority.LocalValue) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
|
|||
this.Log().Debug(string.Format( |
|||
"Bound value of {0}.{1} (#{2:x8})", |
|||
this.GetType().Name, |
|||
property.Name, |
|||
this.GetHashCode())); |
|||
} |
|||
|
|||
/// <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> |
|||
public void Bind<T>( |
|||
PerspexProperty<T> property, |
|||
IObservable<T> source, |
|||
BindingPriority priority = BindingPriority.LocalValue) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
this.Bind((PerspexProperty)property, (IObservable<object>)source, priority); |
|||
} |
|||
|
|||
private static IObservable<object> BoxObservable<T>(IObservable<T> observable) |
|||
{ |
|||
return Observable.Create<object>(observer => |
|||
{ |
|||
return observable.Subscribe(value => |
|||
{ |
|||
observer.OnNext(value); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
private PriorityValue CreatePriorityValue(PerspexProperty property) |
|||
{ |
|||
PriorityValue result = new PriorityValue(); |
|||
|
|||
result.Subscribe(x => |
|||
{ |
|||
object oldValue = (x.Item1 == PerspexProperty.UnsetValue) ? |
|||
this.GetDefaultValue(property) : |
|||
x.Item1; |
|||
object newValue = (x.Item2 == PerspexProperty.UnsetValue) ? |
|||
this.GetDefaultValue(property) : |
|||
x.Item2; |
|||
|
|||
if (!object.Equals(oldValue, newValue)) |
|||
{ |
|||
this.RaisePropertyChanged(property, oldValue, newValue); |
|||
|
|||
this.Log().Debug(string.Format( |
|||
"Set value of {0}.{1} (#{2:x8}) to {3}", |
|||
this.GetType().Name, |
|||
property.Name, |
|||
this.GetHashCode(), |
|||
newValue)); |
|||
} |
|||
}); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private object GetDefaultValue(PerspexProperty property) |
|||
{ |
|||
if (property.Inherits && this.inheritanceParent != null) |
|||
{ |
|||
return this.inheritanceParent.GetValue(property); |
|||
} |
|||
else |
|||
{ |
|||
return property.GetDefaultValue(this.GetType()); |
|||
} |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Called when a property is changed on the current <see cref="InheritanceParent"/>. |
|||
/// </summary> |
|||
/// <param name="sender">The event sender.</param> |
|||
/// <param name="e">The event args.</param> |
|||
/// <remarks> |
|||
/// Checks for changes in an inherited property value. |
|||
/// </remarks> |
|||
private void ParentPropertyChanged(object sender, PerspexPropertyChangedEventArgs e) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(e != null); |
|||
|
|||
if (e.Property.Inherits && !this.IsSet(e.Property)) |
|||
{ |
|||
this.RaisePropertyChanged(e.Property, e.OldValue, e.NewValue); |
|||
} |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Raises the <see cref="PropertyChanged"/> event. |
|||
/// </summary> |
|||
/// <param name="property">The property that has changed.</param> |
|||
/// <param name="oldValue">The old property value.</param> |
|||
/// <param name="newValue">The new property value.</param> |
|||
private void RaisePropertyChanged(PerspexProperty property, object oldValue, object newValue) |
|||
{ |
|||
Contract.Requires<NullReferenceException>(property != null); |
|||
|
|||
if (this.PropertyChanged != null) |
|||
{ |
|||
this.PropertyChanged( |
|||
this, |
|||
new PerspexPropertyChangedEventArgs(property, oldValue, newValue)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue