From da5fd06fcd67301bb3c91e3b6c8f46af67ae00e6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Apr 2016 13:47:15 -0500 Subject: [PATCH] Moved validation filtering into ExpressionObserver and out of InstancedBinding, PerspexObject, and the Priority* system. Renamed some methods in this system to be more descriptive. --- .../Perspex.Markup.Xaml/Data/Binding.cs | 10 +++---- .../Perspex.Markup/Data/ExpressionObserver.cs | 21 +++++++++------ src/Perspex.Base/Data/BindingOperations.cs | 4 +-- src/Perspex.Base/Data/InstancedBinding.cs | 20 +++----------- src/Perspex.Base/IPerspexObject.cs | 8 ++---- src/Perspex.Base/IPriorityValueOwner.cs | 2 +- src/Perspex.Base/PerspexObject.cs | 26 +++++++++---------- src/Perspex.Base/PriorityBindingEntry.cs | 9 ++----- src/Perspex.Base/PriorityLevel.cs | 4 +-- src/Perspex.Base/PriorityValue.cs | 6 ++--- src/Perspex.Controls/Control.cs | 5 ++-- .../Data/BindingTests_TemplatedParent.cs | 6 ++--- .../SelectorTests_Child.cs | 4 +-- .../SelectorTests_Descendent.cs | 4 +-- .../TestControlBase.cs | 4 +-- .../TestTemplatedControl.cs | 4 +-- 16 files changed, 59 insertions(+), 78 deletions(-) diff --git a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs b/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs index 8ce4aa7302..5a3f647013 100644 --- a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs +++ b/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs @@ -131,7 +131,7 @@ namespace Perspex.Markup.Xaml.Data FallbackValue, Priority); - return new InstancedBinding(subject, Mode, Priority, ValidationMethods); + return new InstancedBinding(subject, Mode, Priority); } private static PathInfo ParsePath(string path) @@ -207,7 +207,7 @@ namespace Perspex.Markup.Xaml.Data var result = new ExpressionObserver( () => target.GetValue(Control.DataContextProperty), path, - update); + update, ValidationMethods); return result; } @@ -218,7 +218,7 @@ namespace Perspex.Markup.Xaml.Data .OfType() .Select(x => x.GetObservable(Control.DataContextProperty)) .Switch(), - path); + path, ValidationMethods); } } @@ -228,7 +228,7 @@ namespace Perspex.Markup.Xaml.Data var result = new ExpressionObserver( ControlLocator.Track(target, elementName), - path); + path, ValidationMethods); return result; } @@ -236,7 +236,7 @@ namespace Perspex.Markup.Xaml.Data { Contract.Requires(source != null); - return new ExpressionObserver(source, path); + return new ExpressionObserver(source, path, ValidationMethods); } private ExpressionObserver CreateTemplatedParentObserver( diff --git a/src/Markup/Perspex.Markup/Data/ExpressionObserver.cs b/src/Markup/Perspex.Markup/Data/ExpressionObserver.cs index 082aaf652b..d7fd25d237 100644 --- a/src/Markup/Perspex.Markup/Data/ExpressionObserver.cs +++ b/src/Markup/Perspex.Markup/Data/ExpressionObserver.cs @@ -47,18 +47,20 @@ namespace Perspex.Markup.Data private IDisposable _updateSubscription; private int _count; private readonly ExpressionNode _node; + private ValidationMethods _methods; /// /// Initializes a new instance of the class. /// /// The root object. /// The expression. - public ExpressionObserver(object root, string expression) + /// The validation methods to enable on this observer. + public ExpressionObserver(object root, string expression, ValidationMethods methods = ValidationMethods.None) { Contract.Requires(expression != null); _root = new WeakReference(root); - + _methods = methods; if (!string.IsNullOrWhiteSpace(expression)) { _node = ExpressionNodeBuilder.Build(expression); @@ -72,13 +74,14 @@ namespace Perspex.Markup.Data /// /// An observable which provides the root object. /// The expression. - public ExpressionObserver(IObservable rootObservable, string expression) + /// The validation methods to enable on this observer. + public ExpressionObserver(IObservable rootObservable, string expression, ValidationMethods methods = ValidationMethods.None) { Contract.Requires(rootObservable != null); Contract.Requires(expression != null); _rootObservable = rootObservable; - + _methods = methods; if (!string.IsNullOrWhiteSpace(expression)) { _node = ExpressionNodeBuilder.Build(expression); @@ -93,10 +96,12 @@ namespace Perspex.Markup.Data /// A function which gets the root object. /// The expression. /// An observable which triggers a re-read of the getter. + /// The validation methods to enable on this observer. public ExpressionObserver( Func rootGetter, string expression, - IObservable update) + IObservable update, + ValidationMethods methods = ValidationMethods.None) { Contract.Requires(rootGetter != null); Contract.Requires(expression != null); @@ -104,7 +109,7 @@ namespace Perspex.Markup.Data _rootGetter = rootGetter; _update = update; - + _methods = methods; if (!string.IsNullOrWhiteSpace(expression)) { _node = ExpressionNodeBuilder.Build(expression); @@ -216,8 +221,8 @@ namespace Perspex.Markup.Data { source = source.TakeUntil(_update.LastOrDefaultAsync()); } - - var subscription = source.Subscribe(observer); + var validationFiltered = source.Where(o => (o as ValidationStatus)?.Match(_methods) ?? true); + var subscription = validationFiltered.Subscribe(observer); return Disposable.Create(() => { diff --git a/src/Perspex.Base/Data/BindingOperations.cs b/src/Perspex.Base/Data/BindingOperations.cs index dbeacfbe90..efa0c8500d 100644 --- a/src/Perspex.Base/Data/BindingOperations.cs +++ b/src/Perspex.Base/Data/BindingOperations.cs @@ -44,10 +44,10 @@ namespace Perspex.Data { case BindingMode.Default: case BindingMode.OneWay: - return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority, binding.ValidationMethods); + return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority); case BindingMode.TwoWay: return new CompositeDisposable( - target.Bind(property, binding.Subject, binding.Priority, binding.ValidationMethods), + target.Bind(property, binding.Subject, binding.Priority), target.GetObservable(property).Subscribe(binding.Subject)); case BindingMode.OneTime: var source = binding.Subject ?? binding.Observable; diff --git a/src/Perspex.Base/Data/InstancedBinding.cs b/src/Perspex.Base/Data/InstancedBinding.cs index 3b911fef35..50daf56e97 100644 --- a/src/Perspex.Base/Data/InstancedBinding.cs +++ b/src/Perspex.Base/Data/InstancedBinding.cs @@ -29,16 +29,13 @@ namespace Perspex.Data /// /// The value used for the binding. /// - /// The validation methods for this binding. /// The binding priority. public InstancedBinding(object value, - BindingPriority priority = BindingPriority.LocalValue, - ValidationMethods methods = ValidationMethods.None) + BindingPriority priority = BindingPriority.LocalValue) { Mode = BindingMode.OneTime; Priority = priority; Value = value; - ValidationMethods = methods; } /// @@ -47,12 +44,10 @@ namespace Perspex.Data /// The observable for a one-way binding. /// The binding mode. /// The binding priority. - /// The validation methods for this binding. public InstancedBinding( IObservable observable, BindingMode mode = BindingMode.OneWay, - BindingPriority priority = BindingPriority.LocalValue, - ValidationMethods methods = ValidationMethods.None) + BindingPriority priority = BindingPriority.LocalValue) { Contract.Requires(observable != null); @@ -66,7 +61,6 @@ namespace Perspex.Data Mode = mode; Priority = priority; Observable = observable; - ValidationMethods = methods; } /// @@ -75,19 +69,16 @@ namespace Perspex.Data /// The subject for a two-way binding. /// The binding mode. /// The binding priority. - /// The validation methods for this binding. public InstancedBinding( ISubject subject, BindingMode mode = BindingMode.OneWay, - BindingPriority priority = BindingPriority.LocalValue, - ValidationMethods methods = ValidationMethods.None) + BindingPriority priority = BindingPriority.LocalValue) { Contract.Requires(subject != null); Mode = mode; Priority = priority; Subject = subject; - ValidationMethods = methods; } /// @@ -114,10 +105,5 @@ namespace Perspex.Data /// Gets the subject for a two-way binding. /// public ISubject Subject { get; } - - /// - /// Gets the validation methods for this binding. - /// - public ValidationMethods ValidationMethods { get; } } } diff --git a/src/Perspex.Base/IPerspexObject.cs b/src/Perspex.Base/IPerspexObject.cs index c92898bffb..4290bfe792 100644 --- a/src/Perspex.Base/IPerspexObject.cs +++ b/src/Perspex.Base/IPerspexObject.cs @@ -67,15 +67,13 @@ namespace Perspex /// The property. /// The observable. /// The priority of the binding. - /// The validation methods of the binding. /// /// A disposable which can be used to terminate the binding. /// IDisposable Bind( PerspexProperty property, IObservable source, - BindingPriority priority = BindingPriority.LocalValue, - ValidationMethods validation = ValidationMethods.None); + BindingPriority priority = BindingPriority.LocalValue); /// /// Binds a to an observable. @@ -84,14 +82,12 @@ namespace Perspex /// The property. /// The observable. /// The priority of the binding. - /// The validation methods of the binding. /// /// A disposable which can be used to terminate the binding. /// IDisposable Bind( PerspexProperty property, IObservable source, - BindingPriority priority = BindingPriority.LocalValue, - ValidationMethods validation = ValidationMethods.None); + BindingPriority priority = BindingPriority.LocalValue); } } \ No newline at end of file diff --git a/src/Perspex.Base/IPriorityValueOwner.cs b/src/Perspex.Base/IPriorityValueOwner.cs index 2a3192dfca..1afc005bad 100644 --- a/src/Perspex.Base/IPriorityValueOwner.cs +++ b/src/Perspex.Base/IPriorityValueOwner.cs @@ -23,6 +23,6 @@ namespace Perspex /// /// The source of the change. /// The validation status. - void ValidationChanged(PriorityValue sender, ValidationStatus status); + void DataValidationChanged(PriorityValue sender, ValidationStatus status); } } diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs index 08b91a9afb..4cd6998b76 100644 --- a/src/Perspex.Base/PerspexObject.cs +++ b/src/Perspex.Base/PerspexObject.cs @@ -375,15 +375,13 @@ namespace Perspex /// The property. /// The observable. /// The priority of the binding. - /// The validation methods of the binding. /// /// A disposable which can be used to terminate the binding. /// public IDisposable Bind( PerspexProperty property, IObservable source, - BindingPriority priority = BindingPriority.LocalValue, - ValidationMethods validation = ValidationMethods.None) + BindingPriority priority = BindingPriority.LocalValue) { Contract.Requires(property != null); VerifyAccess(); @@ -412,8 +410,7 @@ namespace Perspex .Subscribe(x => DirectBindingSet(property, x)); validationSubcription = source .OfType() - .Where(v => v.Match(validation)) - .Subscribe(x => ValidationChanged(property, x)); + .Subscribe(x => DataValidation(property, x)); s_directBindings.Add(subscription); @@ -447,7 +444,7 @@ namespace Perspex GetDescription(source), priority); - return v.Add(source, (int)priority, validation); + return v.Add(source, (int)priority); } } @@ -458,19 +455,17 @@ namespace Perspex /// The property. /// The observable. /// The priority of the binding. - /// The validation methods of the binding. /// /// A disposable which can be used to terminate the binding. /// public IDisposable Bind( PerspexProperty property, IObservable source, - BindingPriority priority = BindingPriority.LocalValue, - ValidationMethods validation = ValidationMethods.None) + BindingPriority priority = BindingPriority.LocalValue) { Contract.Requires(property != null); - return Bind((PerspexProperty)property, source.Select(x => (object)x), priority); + return Bind(property, source.Select(x => (object)x), priority); } /// @@ -517,13 +512,18 @@ namespace Perspex } /// - void IPriorityValueOwner.ValidationChanged(PriorityValue sender, ValidationStatus status) + void IPriorityValueOwner.DataValidationChanged(PriorityValue sender, ValidationStatus status) { var property = sender.Property; - ValidationChanged(property, status); + DataValidation(property, status); } - protected virtual void ValidationChanged(PerspexProperty property, ValidationStatus status) + /// + /// Called when the validation state on a tracked property is changed. + /// + /// The property whose validation state changed. + /// The new validation state. + protected virtual void DataValidation(PerspexProperty property, ValidationStatus status) { } diff --git a/src/Perspex.Base/PriorityBindingEntry.cs b/src/Perspex.Base/PriorityBindingEntry.cs index 4f923e3a9f..cc5cd66c2e 100644 --- a/src/Perspex.Base/PriorityBindingEntry.cs +++ b/src/Perspex.Base/PriorityBindingEntry.cs @@ -13,7 +13,6 @@ namespace Perspex { private PriorityLevel _owner; private IDisposable _subscription; - private ValidationMethods _validation; /// /// Initializes a new instance of the class. @@ -23,11 +22,10 @@ namespace Perspex /// The binding index. Later bindings should have higher indexes. /// /// The validation settings for the binding. - public PriorityBindingEntry(PriorityLevel owner, int index, ValidationMethods validation) + public PriorityBindingEntry(PriorityLevel owner, int index) { _owner = owner; Index = index; - _validation = validation; } /// @@ -106,10 +104,7 @@ namespace Perspex if (validationStatus != null) { - if (validationStatus.Match(_validation)) - { - _owner.Validation(this, validationStatus); - } + _owner.Validation(this, validationStatus); } else if (bindingError == null || bindingError.UseFallbackValue) { diff --git a/src/Perspex.Base/PriorityLevel.cs b/src/Perspex.Base/PriorityLevel.cs index 3049c793d9..2ab957f645 100644 --- a/src/Perspex.Base/PriorityLevel.cs +++ b/src/Perspex.Base/PriorityLevel.cs @@ -99,11 +99,11 @@ namespace Perspex /// The binding to add. /// Validation settings for the binding. /// A disposable used to remove the binding. - public IDisposable Add(IObservable binding, ValidationMethods validation) + public IDisposable Add(IObservable binding) { Contract.Requires(binding != null); - var entry = new PriorityBindingEntry(this, _nextIndex++, validation); + var entry = new PriorityBindingEntry(this, _nextIndex++); var node = Bindings.AddFirst(entry); entry.Start(binding); diff --git a/src/Perspex.Base/PriorityValue.cs b/src/Perspex.Base/PriorityValue.cs index 155fa47c06..c0e855654c 100644 --- a/src/Perspex.Base/PriorityValue.cs +++ b/src/Perspex.Base/PriorityValue.cs @@ -81,9 +81,9 @@ namespace Perspex /// /// A disposable that will remove the binding. /// - public IDisposable Add(IObservable binding, int priority, ValidationMethods validation = ValidationMethods.None) + public IDisposable Add(IObservable binding, int priority) { - return GetLevel(priority).Add(binding, validation); + return GetLevel(priority).Add(binding); } /// @@ -186,7 +186,7 @@ namespace Perspex /// The validation status. public void LevelValidation(PriorityLevel priorityLevel, ValidationStatus validationStatus) { - _owner.ValidationChanged(this, validationStatus); + _owner.DataValidationChanged(this, validationStatus); } /// diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs index c4a99d8119..f59811b79c 100644 --- a/src/Perspex.Controls/Control.cs +++ b/src/Perspex.Controls/Control.cs @@ -423,9 +423,10 @@ namespace Perspex.Controls } } - protected override void ValidationChanged(PerspexProperty property, ValidationStatus status) + /// + protected override void DataValidation(PerspexProperty property, ValidationStatus status) { - base.ValidationChanged(property, status); + base.DataValidation(property, status); ValidationStatus = status; } diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs index 9cfcac0825..1979d5d9bf 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs @@ -32,8 +32,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data target.Verify(x => x.Bind( TextBox.TextProperty, It.IsAny>(), - BindingPriority.TemplatedParent, - ValidationMethods.None)); + BindingPriority.TemplatedParent)); } [Fact] @@ -53,8 +52,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data target.Verify(x => x.Bind( TextBox.TextProperty, It.IsAny>(), - BindingPriority.TemplatedParent, - ValidationMethods.None)); + BindingPriority.TemplatedParent)); } private Mock CreateTarget( diff --git a/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs b/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs index b6cb7a0d91..465610b045 100644 --- a/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs +++ b/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs @@ -129,7 +129,7 @@ namespace Perspex.Styling.UnitTests throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority, ValidationMethods validation) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority) { throw new NotImplementedException(); } @@ -139,7 +139,7 @@ namespace Perspex.Styling.UnitTests throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue, ValidationMethods validation = ValidationMethods.None) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { throw new NotImplementedException(); } diff --git a/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs index 83f0672a6f..62c460403b 100644 --- a/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs @@ -160,7 +160,7 @@ namespace Perspex.Styling.UnitTests throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue, ValidationMethods validation = ValidationMethods.None) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { throw new NotImplementedException(); } @@ -170,7 +170,7 @@ namespace Perspex.Styling.UnitTests throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue, ValidationMethods validation = ValidationMethods.None) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { throw new NotImplementedException(); } diff --git a/tests/Perspex.Styling.UnitTests/TestControlBase.cs b/tests/Perspex.Styling.UnitTests/TestControlBase.cs index ac613ac75e..ba9646d5f4 100644 --- a/tests/Perspex.Styling.UnitTests/TestControlBase.cs +++ b/tests/Perspex.Styling.UnitTests/TestControlBase.cs @@ -62,12 +62,12 @@ namespace Perspex.Styling.UnitTests throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue, ValidationMethods validation = ValidationMethods.None) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue, ValidationMethods validation = ValidationMethods.None) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { throw new NotImplementedException(); } diff --git a/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs b/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs index 156fdfa035..b67cfd79a4 100644 --- a/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs +++ b/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs @@ -57,12 +57,12 @@ namespace Perspex.Styling.UnitTests throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue, ValidationMethods validation = ValidationMethods.None) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { throw new NotImplementedException(); } - public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue, ValidationMethods validation = ValidationMethods.None) + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority = BindingPriority.LocalValue) { throw new NotImplementedException(); }