36 changed files with 816 additions and 41 deletions
@ -0,0 +1,74 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Perspex.Data; |
||||
|
|
||||
|
namespace Perspex.Markup.Data.Plugins |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Validates properties that report errors by throwing exceptions.
|
||||
|
/// </summary>
|
||||
|
public class ExceptionValidationCheckerPlugin : IValidationCheckerPlugin |
||||
|
{ |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public bool Match(WeakReference reference) => true; |
||||
|
|
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public ValidationCheckerBase Start(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback) |
||||
|
{ |
||||
|
return new ExceptionValidationChecker(reference, name, accessor, callback); |
||||
|
} |
||||
|
|
||||
|
private class ExceptionValidationChecker : ValidationCheckerBase |
||||
|
{ |
||||
|
public ExceptionValidationChecker(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback) |
||||
|
: base(reference, name, accessor, callback) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override bool SetValue(object value, BindingPriority priority) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var success = base.SetValue(value, priority); |
||||
|
SendValidationCallback(new ExceptionValidationStatus(null)); |
||||
|
return success; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
SendValidationCallback(new ExceptionValidationStatus(ex)); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Describes the current validation status after setting a property value.
|
||||
|
/// </summary>
|
||||
|
public class ExceptionValidationStatus : ValidationStatus |
||||
|
{ |
||||
|
internal ExceptionValidationStatus(Exception exception) |
||||
|
{ |
||||
|
Exception = exception; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The thrown exception. If there was no thrown exception, null.
|
||||
|
/// </summary>
|
||||
|
public Exception Exception { get; } |
||||
|
|
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool IsValid => Exception == null; |
||||
|
|
||||
|
public override bool Match(ValidationMethods enabledMethods) |
||||
|
{ |
||||
|
return (enabledMethods & ValidationMethods.Exceptions) != 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using Perspex.Data; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Perspex.Markup.Data.Plugins |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines how view model data validation is observed by an <see cref="ExpressionObserver"/>.
|
||||
|
/// </summary>
|
||||
|
public interface IValidationCheckerPlugin |
||||
|
{ |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks whether the data uses a validation scheme supported by this plugin.
|
||||
|
/// </summary>
|
||||
|
/// <param name="reference">A weak reference to the data.</param>
|
||||
|
/// <returns><c>true</c> if this plugin can observe the validation; otherwise, <c>false</c>.</returns>
|
||||
|
bool Match(WeakReference reference); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Starts monitering the validation state of an object for the given property.
|
||||
|
/// </summary>
|
||||
|
/// <param name="reference">A weak reference to the object.</param>
|
||||
|
/// <param name="name">The property name.</param>
|
||||
|
/// <param name="accessor">An underlying <see cref="IPropertyAccessor"/> to access the property.</param>
|
||||
|
/// <param name="callback">A function to call when the validation state changes.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="ValidationCheckerBase"/> subclass through which future interactions with the
|
||||
|
/// property will be made.
|
||||
|
/// </returns>
|
||||
|
ValidationCheckerBase Start(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,96 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Perspex.Data; |
||||
|
using System.ComponentModel; |
||||
|
using System.Collections; |
||||
|
using Perspex.Utilities; |
||||
|
|
||||
|
namespace Perspex.Markup.Data.Plugins |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Validates properties on objects that implement <see cref="INotifyDataErrorInfo"/>.
|
||||
|
/// </summary>
|
||||
|
public class IndeiValidationCheckerPlugin : IValidationCheckerPlugin |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public bool Match(WeakReference reference) |
||||
|
{ |
||||
|
return reference.Target is INotifyDataErrorInfo; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public ValidationCheckerBase Start(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback) |
||||
|
{ |
||||
|
return new IndeiValidationChecker(reference, name, accessor, callback); |
||||
|
} |
||||
|
|
||||
|
private class IndeiValidationChecker : ValidationCheckerBase, IWeakSubscriber<DataErrorsChangedEventArgs> |
||||
|
{ |
||||
|
public IndeiValidationChecker(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback) |
||||
|
: base(reference, name, accessor, callback) |
||||
|
{ |
||||
|
var target = reference.Target as INotifyDataErrorInfo; |
||||
|
if (target != null) |
||||
|
{ |
||||
|
if (target.HasErrors) |
||||
|
{ |
||||
|
SendValidationCallback(new IndeiValidationStatus(target.GetErrors(name))); |
||||
|
} |
||||
|
WeakSubscriptionManager.Subscribe( |
||||
|
target, |
||||
|
nameof(target.ErrorsChanged), |
||||
|
this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void Dispose() |
||||
|
{ |
||||
|
base.Dispose(); |
||||
|
var target = _reference.Target as INotifyDataErrorInfo; |
||||
|
if (target != null) |
||||
|
{ |
||||
|
WeakSubscriptionManager.Unsubscribe( |
||||
|
target, |
||||
|
nameof(target.ErrorsChanged), |
||||
|
this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void OnEvent(object sender, DataErrorsChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.PropertyName == _name || string.IsNullOrEmpty(e.PropertyName)) |
||||
|
{ |
||||
|
var indei = _reference.Target as INotifyDataErrorInfo; |
||||
|
SendValidationCallback(new IndeiValidationStatus(indei.GetErrors(e.PropertyName))); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Describes the current validation status of a property as reported by an object that implements <see cref="INotifyDataErrorInfo"/>.
|
||||
|
/// </summary>
|
||||
|
public class IndeiValidationStatus : ValidationStatus |
||||
|
{ |
||||
|
internal IndeiValidationStatus(IEnumerable errors) |
||||
|
{ |
||||
|
Errors = errors; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool IsValid => !Errors.OfType<object>().Any(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The errors on the given property and on the object as a whole.
|
||||
|
/// </summary>
|
||||
|
public IEnumerable Errors { get; } |
||||
|
|
||||
|
public override bool Match(ValidationMethods enabledMethods) |
||||
|
{ |
||||
|
return (enabledMethods & ValidationMethods.INotifyDataErrorInfo) != 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using Perspex.Data; |
||||
|
|
||||
|
namespace Perspex.Markup.Data.Plugins |
||||
|
{ |
||||
|
public abstract class ValidationCheckerBase : IPropertyAccessor |
||||
|
{ |
||||
|
protected readonly WeakReference _reference; |
||||
|
protected readonly string _name; |
||||
|
private readonly IPropertyAccessor _accessor; |
||||
|
private readonly Action<ValidationStatus> _callback; |
||||
|
|
||||
|
protected ValidationCheckerBase(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback) |
||||
|
{ |
||||
|
_reference = reference; |
||||
|
_name = name; |
||||
|
_accessor = accessor; |
||||
|
_callback = callback; |
||||
|
} |
||||
|
|
||||
|
public Type PropertyType => _accessor.PropertyType; |
||||
|
|
||||
|
public object Value => _accessor.Value; |
||||
|
|
||||
|
public virtual void Dispose() => _accessor.Dispose(); |
||||
|
|
||||
|
public virtual bool SetValue(object value, BindingPriority priority) => _accessor.SetValue(value, priority); |
||||
|
|
||||
|
protected void SendValidationCallback(ValidationStatus status) |
||||
|
{ |
||||
|
_callback?.Invoke(status); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Perspex.Data |
||||
|
{ |
||||
|
[Flags] |
||||
|
public enum ValidationMethods |
||||
|
{ |
||||
|
None = 0, |
||||
|
Exceptions = 1, |
||||
|
INotifyDataErrorInfo = 2, |
||||
|
All = -1 |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
// 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.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Perspex.Data |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Contains information on if the current object passed validation.
|
||||
|
/// Subclasses of this class contain additional information depending on the method of validation checking.
|
||||
|
/// </summary>
|
||||
|
public abstract class ValidationStatus |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// True when the data passes validation; otherwise, false.
|
||||
|
/// </summary>
|
||||
|
public abstract bool IsValid { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks if this validation status came from a currently enabled method of validation checking.
|
||||
|
/// </summary>
|
||||
|
/// <param name="enabledMethods">The enabled methods of validation checking.</param>
|
||||
|
/// <returns>True if enabled; otherwise, false.</returns>
|
||||
|
public abstract bool Match(ValidationMethods enabledMethods); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using Perspex.Data; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class ControlValidationStatus : ValidationStatus, INotifyPropertyChanged |
||||
|
{ |
||||
|
private Dictionary<Type, ValidationStatus> propertyValidation = new Dictionary<Type, ValidationStatus>(); |
||||
|
|
||||
|
public override bool IsValid => propertyValidation.Values.All(status => status.IsValid); |
||||
|
|
||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
|
||||
|
public override bool Match(ValidationMethods enabledMethods) => true; |
||||
|
|
||||
|
public void UpdateValidationStatus(ValidationStatus status) |
||||
|
{ |
||||
|
propertyValidation[status.GetType()] = status; |
||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("")); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
// 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 Perspex.Data; |
||||
|
using Perspex.Markup.Data.Plugins; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Markup.UnitTests.Data |
||||
|
{ |
||||
|
public class ExceptionValidatorTests |
||||
|
{ |
||||
|
public class Data : INotifyPropertyChanged |
||||
|
{ |
||||
|
private int nonValidated; |
||||
|
|
||||
|
public int NonValidated |
||||
|
{ |
||||
|
get { return nonValidated; } |
||||
|
set { nonValidated = value; NotifyPropertyChanged(); } |
||||
|
} |
||||
|
|
||||
|
private int mustBePositive; |
||||
|
|
||||
|
public int MustBePositive |
||||
|
{ |
||||
|
get { return mustBePositive; } |
||||
|
set |
||||
|
{ |
||||
|
if (value <= 0) |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException(nameof(value)); |
||||
|
} |
||||
|
mustBePositive = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
|
||||
|
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") |
||||
|
{ |
||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Non_Validating_Triggers_Validation() |
||||
|
{ |
||||
|
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); |
||||
|
var validatorPlugin = new ExceptionValidationCheckerPlugin(); |
||||
|
var data = new Data(); |
||||
|
var accessor = inpcAccessorPlugin.Start(new WeakReference(data), nameof(data.NonValidated), _ => { }); |
||||
|
ValidationStatus status = null; |
||||
|
var validator = validatorPlugin.Start(new WeakReference(data), nameof(data.NonValidated), accessor, s => status = s); |
||||
|
|
||||
|
validator.SetValue(5, BindingPriority.LocalValue); |
||||
|
|
||||
|
Assert.NotNull(status); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Validating_Property_To_Valid_Value_Returns_Successful_ValidationStatus() |
||||
|
{ |
||||
|
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); |
||||
|
var validatorPlugin = new ExceptionValidationCheckerPlugin(); |
||||
|
var data = new Data(); |
||||
|
var accessor = inpcAccessorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), _ => { }); |
||||
|
ValidationStatus status = null; |
||||
|
var validator = validatorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), accessor, s => status = s); |
||||
|
|
||||
|
validator.SetValue(5, BindingPriority.LocalValue); |
||||
|
|
||||
|
Assert.True(status.IsValid); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Validating_Property_To_Invalid_Value_Returns_Failed_ValidationStatus() |
||||
|
{ |
||||
|
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); |
||||
|
var validatorPlugin = new ExceptionValidationCheckerPlugin(); |
||||
|
var data = new Data(); |
||||
|
var accessor = inpcAccessorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), _ => { }); |
||||
|
ValidationStatus status = null; |
||||
|
var validator = validatorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), accessor, s => status = s); |
||||
|
|
||||
|
validator.SetValue(-5, BindingPriority.LocalValue); |
||||
|
|
||||
|
Assert.False(status.IsValid); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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 Perspex.Data; |
||||
|
using Perspex.Markup.Data.Plugins; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Xunit; |
||||
|
using System.Collections; |
||||
|
|
||||
|
namespace Perspex.Markup.UnitTests.Data |
||||
|
{ |
||||
|
public class IndeiValidatorTests |
||||
|
{ |
||||
|
public class Data : INotifyPropertyChanged, INotifyDataErrorInfo |
||||
|
{ |
||||
|
private int nonValidated; |
||||
|
|
||||
|
public int NonValidated |
||||
|
{ |
||||
|
get { return nonValidated; } |
||||
|
set { nonValidated = value; NotifyPropertyChanged(); } |
||||
|
} |
||||
|
|
||||
|
private int mustBePositive; |
||||
|
|
||||
|
public int MustBePositive |
||||
|
{ |
||||
|
get { return mustBePositive; } |
||||
|
set |
||||
|
{ |
||||
|
mustBePositive = value; |
||||
|
NotifyErrorsChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool HasErrors |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return MustBePositive > 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; |
||||
|
|
||||
|
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") |
||||
|
{ |
||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||
|
} |
||||
|
|
||||
|
private void NotifyErrorsChanged([CallerMemberName] string propertyName = "") |
||||
|
{ |
||||
|
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); |
||||
|
} |
||||
|
|
||||
|
public IEnumerable GetErrors(string propertyName) |
||||
|
{ |
||||
|
if (propertyName == nameof(MustBePositive) && MustBePositive <= 0) |
||||
|
{ |
||||
|
yield return $"{nameof(MustBePositive)} must be positive"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Non_Validating_Does_Not_Trigger_Validation() |
||||
|
{ |
||||
|
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); |
||||
|
var validatorPlugin = new IndeiValidationCheckerPlugin(); |
||||
|
var data = new Data(); |
||||
|
var accessor = inpcAccessorPlugin.Start(new WeakReference(data), nameof(data.NonValidated), _ => { }); |
||||
|
ValidationStatus status = null; |
||||
|
var validator = validatorPlugin.Start(new WeakReference(data), nameof(data.NonValidated), accessor, s => status = s); |
||||
|
|
||||
|
validator.SetValue(5, BindingPriority.LocalValue); |
||||
|
|
||||
|
Assert.Null(status); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Validating_Property_To_Valid_Value_Returns_Successful_ValidationStatus() |
||||
|
{ |
||||
|
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); |
||||
|
var validatorPlugin = new IndeiValidationCheckerPlugin(); |
||||
|
var data = new Data(); |
||||
|
var accessor = inpcAccessorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), _ => { }); |
||||
|
ValidationStatus status = null; |
||||
|
var validator = validatorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), accessor, s => status = s); |
||||
|
|
||||
|
validator.SetValue(5, BindingPriority.LocalValue); |
||||
|
|
||||
|
Assert.True(status.IsValid); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Validating_Property_To_Invalid_Value_Returns_Failed_ValidationStatus() |
||||
|
{ |
||||
|
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); |
||||
|
var validatorPlugin = new IndeiValidationCheckerPlugin(); |
||||
|
var data = new Data(); |
||||
|
var accessor = inpcAccessorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), _ => { }); |
||||
|
ValidationStatus status = null; |
||||
|
var validator = validatorPlugin.Start(new WeakReference(data), nameof(data.MustBePositive), accessor, s => status = s); |
||||
|
|
||||
|
validator.SetValue(-5, BindingPriority.LocalValue); |
||||
|
|
||||
|
Assert.False(status.IsValid); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
using Perspex.Controls; |
||||
|
using Perspex.Markup.Xaml.Data; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.UnitTests.Data |
||||
|
{ |
||||
|
public class BindingTests_Validation |
||||
|
{ |
||||
|
public class Data : INotifyPropertyChanged |
||||
|
{ |
||||
|
private string mustbeNonEmpty; |
||||
|
|
||||
|
public string MustBeNonEmpty |
||||
|
{ |
||||
|
get { return mustbeNonEmpty; } |
||||
|
set |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(value)) |
||||
|
{ |
||||
|
throw new ArgumentException(nameof(value)); |
||||
|
} |
||||
|
mustbeNonEmpty = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
|
||||
|
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") |
||||
|
{ |
||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Disabled_Validation_Should_Not_Trigger_Validation_Change_Direct() |
||||
|
{ |
||||
|
var source = new Data { MustBeNonEmpty = "Test" }; |
||||
|
var target = new TextBlock { DataContext = source }; |
||||
|
var binding = new Binding |
||||
|
{ |
||||
|
Path = nameof(source.MustBeNonEmpty), |
||||
|
Mode = Perspex.Data.BindingMode.TwoWay, |
||||
|
ValidationMethods = Perspex.Data.ValidationMethods.None |
||||
|
}; |
||||
|
target.Bind(TextBlock.TextProperty, binding); |
||||
|
|
||||
|
target.Text = ""; |
||||
|
|
||||
|
Assert.True(target.ValidationStatus.IsValid); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Enabled_Validation_Should_Trigger_Validation_Change_Direct() |
||||
|
{ |
||||
|
var source = new Data { MustBeNonEmpty = "Test" }; |
||||
|
var target = new TextBlock { DataContext = source }; |
||||
|
var binding = new Binding |
||||
|
{ |
||||
|
Path = nameof(source.MustBeNonEmpty), |
||||
|
Mode = Perspex.Data.BindingMode.TwoWay, |
||||
|
ValidationMethods = Perspex.Data.ValidationMethods.All |
||||
|
}; |
||||
|
target.Bind(TextBlock.TextProperty, binding); |
||||
|
|
||||
|
target.Text = ""; |
||||
|
Assert.False(target.ValidationStatus.IsValid); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Disabled_Validation_Should_Not_Trigger_Validation_Change_Styled() |
||||
|
{ |
||||
|
var source = new Data { MustBeNonEmpty = "Test" }; |
||||
|
var target = new TextBlock { DataContext = source }; |
||||
|
var binding = new Binding |
||||
|
{ |
||||
|
Path = nameof(source.MustBeNonEmpty), |
||||
|
Mode = Perspex.Data.BindingMode.TwoWay, |
||||
|
ValidationMethods = Perspex.Data.ValidationMethods.None |
||||
|
}; |
||||
|
target.Bind(Control.TagProperty, binding); |
||||
|
|
||||
|
target.Tag = ""; |
||||
|
|
||||
|
Assert.True(target.ValidationStatus.IsValid); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Enabled_Validation_Should_Trigger_Validation_Change_Styled() |
||||
|
{ |
||||
|
var source = new Data { MustBeNonEmpty = "Test" }; |
||||
|
var target = new TextBlock { DataContext = source }; |
||||
|
var binding = new Binding |
||||
|
{ |
||||
|
Path = nameof(source.MustBeNonEmpty), |
||||
|
Mode = Perspex.Data.BindingMode.TwoWay, |
||||
|
ValidationMethods = Perspex.Data.ValidationMethods.All |
||||
|
}; |
||||
|
target.Bind(Control.TagProperty, binding); |
||||
|
|
||||
|
target.Tag = ""; |
||||
|
Assert.False(target.ValidationStatus.IsValid); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue