From c984a504a2841c94047012342f6e81ca6e3aa26b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 1 Apr 2016 22:18:19 +0200 Subject: [PATCH] Change the way PriorityValue notifies. Directly call a Changed method on the PriorityValue owner rather than using an observable. --- src/Perspex.Base/IPriorityValueOwner.cs | 19 ++++++ src/Perspex.Base/Perspex.Base.csproj | 1 + src/Perspex.Base/PerspexObject.cs | 56 ++++++++++-------- src/Perspex.Base/PriorityValue.cs | 36 ++++------- src/Perspex.Base/Properties/AssemblyInfo.cs | 3 +- .../Perspex.Base.UnitTests.csproj | 4 ++ .../PriorityValueTests.cs | 59 ++++++++++--------- tests/Perspex.Base.UnitTests/packages.config | 1 + 8 files changed, 101 insertions(+), 78 deletions(-) create mode 100644 src/Perspex.Base/IPriorityValueOwner.cs diff --git a/src/Perspex.Base/IPriorityValueOwner.cs b/src/Perspex.Base/IPriorityValueOwner.cs new file mode 100644 index 0000000000..aa79864794 --- /dev/null +++ b/src/Perspex.Base/IPriorityValueOwner.cs @@ -0,0 +1,19 @@ +// 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 +{ + /// + /// An owner of a . + /// + internal interface IPriorityValueOwner + { + /// + /// Called when a 's value changes. + /// + /// The source of the change. + /// The old value. + /// The new value. + void Changed(PriorityValue sender, object oldValue, object newValue); + } +} diff --git a/src/Perspex.Base/Perspex.Base.csproj b/src/Perspex.Base/Perspex.Base.csproj index ae185b3abf..5718c9e217 100644 --- a/src/Perspex.Base/Perspex.Base.csproj +++ b/src/Perspex.Base/Perspex.Base.csproj @@ -54,6 +54,7 @@ + diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs index 32f8eedc91..e787c97f57 100644 --- a/src/Perspex.Base/PerspexObject.cs +++ b/src/Perspex.Base/PerspexObject.cs @@ -21,7 +21,7 @@ namespace Perspex /// /// This class is analogous to DependencyObject in WPF. /// - public class PerspexObject : IPerspexObject, IPerspexObjectDebug, INotifyPropertyChanged + public class PerspexObject : IPerspexObject, IPerspexObjectDebug, INotifyPropertyChanged, IPriorityValueOwner { /// /// Maintains a list of direct property binding subscriptions so that the binding source @@ -477,6 +477,34 @@ namespace Perspex } } + /// + void IPriorityValueOwner.Changed(PriorityValue sender, object oldValue, object newValue) + { + var property = sender.Property; + var priority = (BindingPriority)sender.ValuePriority; + + oldValue = (oldValue == PerspexProperty.UnsetValue) ? + GetDefaultValue(property) : + oldValue; + newValue = (newValue == PerspexProperty.UnsetValue) ? + GetDefaultValue(property) : + newValue; + + if (!Equals(oldValue, newValue)) + { + RaisePropertyChanged(property, oldValue, newValue, priority); + + Logger.Verbose( + LogArea.Property, + this, + "{Property} changed from {$Old} to {$Value} with priority {Priority}", + property, + oldValue, + newValue, + priority); + } + } + /// Delegate[] IPerspexObjectDebug.GetPropertyChangedSubscribers() { @@ -604,34 +632,10 @@ namespace Perspex PriorityValue result = new PriorityValue( this, - property.Name, + property, property.PropertyType, validate2); - result.Changed.Subscribe(x => - { - object oldValue = (x.Item1 == PerspexProperty.UnsetValue) ? - GetDefaultValue(property) : - x.Item1; - object newValue = (x.Item2 == PerspexProperty.UnsetValue) ? - GetDefaultValue(property) : - x.Item2; - - if (!Equals(oldValue, newValue)) - { - RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)result.ValuePriority); - - Logger.Verbose( - LogArea.Property, - this, - "{Property} changed from {$Old} to {$Value} with priority {Priority}", - property, - oldValue, - newValue, - (BindingPriority)result.ValuePriority); - } - }); - return result; } diff --git a/src/Perspex.Base/PriorityValue.cs b/src/Perspex.Base/PriorityValue.cs index 7e10c6ca72..5a1ae7a4e0 100644 --- a/src/Perspex.Base/PriorityValue.cs +++ b/src/Perspex.Base/PriorityValue.cs @@ -20,20 +20,16 @@ namespace Perspex /// represent higher priorites. The current is selected from the highest /// priority binding that doesn't return . Where there /// are multiple bindings registered with the same priority, the most recently added binding - /// has a higher priority. Each time the value changes, the observable is - /// fired with the old and new values. + /// has a higher priority. Each time the value changes, the + /// method on the + /// owner object is fired with the old and new values. /// internal class PriorityValue { /// /// The owner of the object. /// - private readonly PerspexObject _owner; - - /// - /// The name of the property. - /// - private readonly string _name; + private readonly IPriorityValueOwner _owner; /// /// The value type. @@ -45,11 +41,6 @@ namespace Perspex /// private readonly Dictionary _levels = new Dictionary(); - /// - /// The changed observable. - /// - private readonly Subject> _changed = new Subject>(); - /// /// The current value. /// @@ -64,17 +55,17 @@ namespace Perspex /// Initializes a new instance of the class. /// /// The owner of the object. - /// The name of the property. + /// The property that the value represents. /// The value type. /// An optional validation function. public PriorityValue( - PerspexObject owner, - string name, + IPriorityValueOwner owner, + PerspexProperty property, Type valueType, Func validate = null) { _owner = owner; - _name = name; + Property = property; _valueType = valueType; _value = PerspexProperty.UnsetValue; ValuePriority = int.MaxValue; @@ -82,12 +73,9 @@ namespace Perspex } /// - /// Fired whenever the current changes. + /// Gets the property that the value represents. /// - /// - /// The old and new values may be the same, this class does not check for distinct values. - /// - public IObservable> Changed => _changed; + public PerspexProperty Property { get; } /// /// Gets the current value. @@ -236,7 +224,7 @@ namespace Perspex ValuePriority = priority; _value = castValue; - _changed.OnNext(Tuple.Create(old, _value)); + _owner?.Changed(this, old, _value); } else { @@ -244,7 +232,7 @@ namespace Perspex LogArea.Property, _owner, "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})", - _name, + Property.Name, _valueType, value, value.GetType()); diff --git a/src/Perspex.Base/Properties/AssemblyInfo.cs b/src/Perspex.Base/Properties/AssemblyInfo.cs index 44faed797a..f3c46631b7 100644 --- a/src/Perspex.Base/Properties/AssemblyInfo.cs +++ b/src/Perspex.Base/Properties/AssemblyInfo.cs @@ -5,4 +5,5 @@ using System.Reflection; using System.Runtime.CompilerServices; [assembly: AssemblyTitle("Perspex.Base")] -[assembly: InternalsVisibleTo("Perspex.Base.UnitTests")] \ No newline at end of file +[assembly: InternalsVisibleTo("Perspex.Base.UnitTests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj index 879022611c..3f1afa52f3 100644 --- a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj +++ b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj @@ -44,6 +44,10 @@ True + + ..\..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll + True + ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll diff --git a/tests/Perspex.Base.UnitTests/PriorityValueTests.cs b/tests/Perspex.Base.UnitTests/PriorityValueTests.cs index 10f3bdb4db..7ae08c1c4c 100644 --- a/tests/Perspex.Base.UnitTests/PriorityValueTests.cs +++ b/tests/Perspex.Base.UnitTests/PriorityValueTests.cs @@ -5,16 +5,23 @@ using System; using System.Linq; using System.Reactive.Linq; using System.Reactive.Subjects; +using Moq; using Xunit; namespace Perspex.Base.UnitTests { public class PriorityValueTests { + private static readonly PerspexProperty TestProperty = + new StyledProperty( + "Test", + typeof(PriorityValueTests), + new StyledPropertyMetadata()); + [Fact] public void Initial_Value_Should_Be_UnsetValue() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); Assert.Same(PerspexProperty.UnsetValue, target.Value); } @@ -22,7 +29,7 @@ namespace Perspex.Base.UnitTests [Fact] public void First_Binding_Sets_Value() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); target.Add(Single("foo"), 0); @@ -32,7 +39,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Changing_Binding_Should_Set_Value() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var subject = new BehaviorSubject("foo"); target.Add(subject, 0); @@ -44,7 +51,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Setting_Direct_Value_Should_Override_Binding() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); target.Add(Single("foo"), 0); target.SetValue("bar", 0); @@ -55,7 +62,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Binding_Firing_Should_Override_Direct_Value() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var source = new BehaviorSubject("initial"); target.Add(source, 0); @@ -69,7 +76,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Earlier_Binding_Firing_Should_Not_Override_Later() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var nonActive = new BehaviorSubject("na"); var source = new BehaviorSubject("initial"); @@ -85,7 +92,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Binding_Completing_Should_Revert_To_Direct_Value() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var source = new BehaviorSubject("initial"); target.Add(source, 0); @@ -101,7 +108,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Binding_With_Lower_Priority_Has_Precedence() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); target.Add(Single("foo"), 1); target.Add(Single("bar"), 0); @@ -113,7 +120,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Later_Binding_With_Same_Priority_Should_Take_Precedence() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); target.Add(Single("foo"), 1); target.Add(Single("bar"), 0); @@ -126,7 +133,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Changing_Binding_With_Lower_Priority_Should_Set_Not_Value() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var subject = new BehaviorSubject("bar"); target.Add(Single("foo"), 0); @@ -139,7 +146,7 @@ namespace Perspex.Base.UnitTests [Fact] public void UnsetValue_Should_Fall_Back_To_Next_Binding() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var subject = new BehaviorSubject("bar"); target.Add(subject, 0); @@ -155,33 +162,31 @@ namespace Perspex.Base.UnitTests [Fact] public void Adding_Value_Should_Call_OnNext() { - var target = new PriorityValue(null, "Test", typeof(string)); - bool called = false; + var owner = new Mock(); + var target = new PriorityValue(owner.Object, TestProperty, typeof(string)); - target.Changed.Subscribe(value => called = value.Item1 == PerspexProperty.UnsetValue && (string)value.Item2 == "foo"); target.Add(Single("foo"), 0); - Assert.True(called); + owner.Verify(x => x.Changed(target, PerspexProperty.UnsetValue, "foo")); } [Fact] public void Changing_Value_Should_Call_OnNext() { - var target = new PriorityValue(null, "Test", typeof(string)); + var owner = new Mock(); + var target = new PriorityValue(owner.Object, TestProperty, typeof(string)); var subject = new BehaviorSubject("foo"); - bool called = false; target.Add(subject, 0); - target.Changed.Subscribe(value => called = (string)value.Item1 == "foo" && (string)value.Item2 == "bar"); subject.OnNext("bar"); - Assert.True(called); + owner.Verify(x => x.Changed(target, "foo", "bar")); } [Fact] public void Disposing_A_Binding_Should_Revert_To_Next_Value() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); target.Add(Single("foo"), 0); var disposable = target.Add(Single("bar"), 0); @@ -194,7 +199,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Disposing_A_Binding_Should_Remove_BindingEntry() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); target.Add(Single("foo"), 0); var disposable = target.Add(Single("bar"), 0); @@ -207,7 +212,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Completing_A_Binding_Should_Revert_To_Previous_Binding() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var source = new BehaviorSubject("bar"); target.Add(Single("foo"), 0); @@ -221,7 +226,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Completing_A_Binding_Should_Revert_To_Lower_Priority() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var source = new BehaviorSubject("bar"); target.Add(Single("foo"), 1); @@ -235,7 +240,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Completing_A_Binding_Should_Remove_BindingEntry() { - var target = new PriorityValue(null, "Test", typeof(string)); + var target = new PriorityValue(null, TestProperty, typeof(string)); var subject = new BehaviorSubject("bar"); target.Add(Single("foo"), 0); @@ -249,7 +254,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Direct_Value_Should_Be_Coerced() { - var target = new PriorityValue(null, "Test", typeof(int), x => Math.Min((int)x, 10)); + var target = new PriorityValue(null, TestProperty, typeof(int), x => Math.Min((int)x, 10)); target.SetValue(5, 0); Assert.Equal(5, target.Value); @@ -260,7 +265,7 @@ namespace Perspex.Base.UnitTests [Fact] public void Bound_Value_Should_Be_Coerced() { - var target = new PriorityValue(null, "Test", typeof(int), x => Math.Min((int)x, 10)); + var target = new PriorityValue(null, TestProperty, typeof(int), x => Math.Min((int)x, 10)); var source = new Subject(); target.Add(source, 0); @@ -274,7 +279,7 @@ namespace Perspex.Base.UnitTests public void Revalidate_Should_ReCoerce_Value() { var max = 10; - var target = new PriorityValue(null, "Test", typeof(int), x => Math.Min((int)x, max)); + var target = new PriorityValue(null, TestProperty, typeof(int), x => Math.Min((int)x, max)); var source = new Subject(); target.Add(source, 0); diff --git a/tests/Perspex.Base.UnitTests/packages.config b/tests/Perspex.Base.UnitTests/packages.config index be674ecc80..c5e3427b0d 100644 --- a/tests/Perspex.Base.UnitTests/packages.config +++ b/tests/Perspex.Base.UnitTests/packages.config @@ -1,5 +1,6 @@  +