From 6b68a7046bda6509336e152450afd92593490ace Mon Sep 17 00:00:00 2001 From: Alex Helms Date: Mon, 21 Oct 2024 13:54:43 -0700 Subject: [PATCH] Use Community Toolkit's ObservableValidator as the base class for Screen. - Screen derives from ObservableValidator. - Replace SetAndNotify with SetProperty. - Remove PropertyChangedDispatcher. - Remove PropertyChangedBase and use ObservableObject in its place. - Remove ValidatingModelBase. - Remove IValidationAdapter. - Remove IModelValidator. - Fix up tests. - Remove .NET 4.5.2, dotnet 3.0, and dotnet 3.1 target frameworks. - Add .NET 4.6.2, dotnet 6, and dotnet 8 target frameworks required for Community Toolkit. --- README.md | 16 +- Stylet.sln | 5 +- Stylet/ConductorBaseWithActiveItem.cs | 2 +- Stylet/IValidationAdapter.cs | 53 --- Stylet/LabelledValue.cs | 7 +- Stylet/PropertyChangedBase.cs | 96 ----- Stylet/Screen.cs | 21 +- Stylet/Stylet.csproj | 6 +- Stylet/ValidatingModelBase.cs | 363 ----------------- StyletIntegrationTests/ShellViewModel.cs | 2 +- .../StyletIntegrationTests.csproj | 2 +- StyletUnitTests/CommandActionTests.cs | 7 +- StyletUnitTests/ConductorAllActiveTests.cs | 2 +- StyletUnitTests/ConductorNavigatingTests.cs | 2 +- StyletUnitTests/ConductorOneActiveTests.cs | 2 +- StyletUnitTests/ConductorTests.cs | 2 +- StyletUnitTests/LabelledValueTests.cs | 1 - StyletUnitTests/MessageBoxTests.cs | 2 +- StyletUnitTests/PropertyChangedBaseTests.cs | 124 ------ .../PropertyChangedExtensionsTests.cs | 11 +- StyletUnitTests/ScreenTests.cs | 17 +- StyletUnitTests/StyletUnitTests.csproj | 2 +- StyletUnitTests/ValidatingModelBaseTests.cs | 372 ------------------ 23 files changed, 44 insertions(+), 1073 deletions(-) delete mode 100644 Stylet/IValidationAdapter.cs delete mode 100644 Stylet/PropertyChangedBase.cs delete mode 100644 Stylet/ValidatingModelBase.cs delete mode 100644 StyletUnitTests/PropertyChangedBaseTests.cs delete mode 100644 StyletUnitTests/ValidatingModelBaseTests.cs diff --git a/README.md b/README.md index aa67ce2..69f66e0 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ It is inspired by [Caliburn.Micro](http://caliburnmicro.com/), and shares many o Getting Started --------------- -### .NET 5.0+ / .NET Core +### .NET 6.0+ / .NET Core -For .NET Core and .NET 5.0+ projects, the quickest way to get started is by using `dotnet new` with Stylet's template. +For .NET Core and .NET 6.0+ projects, the quickest way to get started is by using `dotnet new` with Stylet's template. Open a command window where you want to create your new project, and install the Stylet templates using: @@ -31,7 +31,7 @@ Open a command window where you want to create your new project, and install the dotnet new -i Stylet.Templates ``` -Then create a new .NET 5.0 project with: +Then create a new .NET 6.0 project with: ``` dotnet new stylet -n MyStyletProject @@ -39,15 +39,9 @@ dotnet new stylet -n MyStyletProject (changing `MyStyletProject` as appropriate). -If you want to create a .NET Core 3.1 project, then: - -``` -dotnet new stylet -F netcoreapp3.1 -n MyStyletProject -``` - If you want to set up your project manually, install the [Stylet](https://www.nuget.org/packages/Stylet) package, then follow the instructions in the [Quick Start](https://github.com/canton7/Stylet/wiki/Quick-Start). -Stylet requires .NET 5.0+ or .NET Core 3.0+. +Stylet requires .NET 6.0+. ### .NET Framework (<= .NET 4) @@ -59,7 +53,7 @@ See [Quick Start](https://github.com/canton7/Stylet/wiki/Quick-Start) for more d If you want to set up your project manually, install the [Stylet](https://www.nuget.org/packages/Stylet) package, then follow the instructions in the [Quick Start](https://github.com/canton7/Stylet/wiki/Quick-Start). -Stylet requires .NET 4.5.2 (Visual Studio 2012 or higher). +Stylet requires .NET 4.6.2 (Visual Studio 2013 or higher). Documentation diff --git a/Stylet.sln b/Stylet.sln index 39df9c3..1d12f6f 100644 --- a/Stylet.sln +++ b/Stylet.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29021.104 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StyletUnitTests", "StyletUnitTests\StyletUnitTests.csproj", "{13AFA20D-CCEA-4A58-920E-4D8816C7296B}" EndProject @@ -43,7 +43,6 @@ Global {895A0541-84CF-4D3D-9539-58F1FC286336}.Release|x64.ActiveCfg = Release|Any CPU {895A0541-84CF-4D3D-9539-58F1FC286336}.Release|x64.Build.0 = Release|Any CPU {4122F924-6B71-4DDA-995E-FAF78242E20C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4122F924-6B71-4DDA-995E-FAF78242E20C}.Debug|Any CPU.Build.0 = Debug|Any CPU {4122F924-6B71-4DDA-995E-FAF78242E20C}.Debug|x64.ActiveCfg = Debug|Any CPU {4122F924-6B71-4DDA-995E-FAF78242E20C}.Debug|x64.Build.0 = Debug|Any CPU {4122F924-6B71-4DDA-995E-FAF78242E20C}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Stylet/ConductorBaseWithActiveItem.cs b/Stylet/ConductorBaseWithActiveItem.cs index 4ca0bad..e8174be 100644 --- a/Stylet/ConductorBaseWithActiveItem.cs +++ b/Stylet/ConductorBaseWithActiveItem.cs @@ -51,7 +51,7 @@ protected virtual void ChangeActiveItem(T newItem, bool closePrevious) ScreenExtensions.TryDeactivate(newItem); } - this.NotifyOfPropertyChange(nameof(this.ActiveItem)); + this.OnPropertyChanged(nameof(this.ActiveItem)); } /// diff --git a/Stylet/IValidationAdapter.cs b/Stylet/IValidationAdapter.cs deleted file mode 100644 index d890aeb..0000000 --- a/Stylet/IValidationAdapter.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Stylet; - -/// -/// Generic version of IValidationArapter. Provided for use with StyletIoC -/// -/// -/// Having a generic version allows you implement it using a generic ModelValidator (ModelValidator{T} : IModelValidator{T}) -/// then write a binding rule like this: -/// builder.Bind(typeof(IModelValidator{})).ToAllImplementations() -/// and request a new IModelValidator{MyViewModelType} in your ViewModel's constructor. -/// -/// Type of model being validated -public interface IModelValidator : IModelValidator -{ -} - -/// -/// Adapter used by ValidationModelBase to perform validation. -/// -/// -/// This should be specialised to the particular ValidationModelBase instance it's validating -/// -public interface IModelValidator -{ - /// - /// Called by ValidatingModelBase, which passes in an instance of itself. - /// This allows the IModelValidator to specialize to validating that particular ValidatingModelBase instance - /// - /// Subject to initialize - void Initialize(object subject); - - /// - /// Validate a single property by name, and return an array of validation errors for that property (or null if validation was successful) - /// - /// Property to validate, or to validate the entire model - /// Array of validation errors, or null / empty if validation was successful - Task> ValidatePropertyAsync(string propertyName); - - /// - /// Validate all properties, and return the results for all properties - /// - /// - /// Use a key of to indicate validation errors for the entire model. - /// - /// If a property validates successfully, you MUST return a null entry for it in the returned dictionary! - /// - /// A dictionary of property name => array of validation errors (or null if that property validated successfully) - Task>> ValidateAllPropertiesAsync(); -} diff --git a/Stylet/LabelledValue.cs b/Stylet/LabelledValue.cs index 5a43849..2239009 100644 --- a/Stylet/LabelledValue.cs +++ b/Stylet/LabelledValue.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using CommunityToolkit.Mvvm.ComponentModel; namespace Stylet; @@ -7,7 +8,7 @@ namespace Stylet; /// Key-value pair useful for attaching labels to objects and displaying them in the view /// /// Type of the value -public class LabelledValue : PropertyChangedBase, IEquatable> +public class LabelledValue : ObservableObject, IEquatable> { private string _label; @@ -17,7 +18,7 @@ public class LabelledValue : PropertyChangedBase, IEquatable public string Label { get => this._label; - set => this.SetAndNotify(ref this._label, value); + set => this.SetProperty(ref this._label, value); } private T _value; @@ -28,7 +29,7 @@ public string Label public T Value { get => this._value; - set => this.SetAndNotify(ref this._value, value); + set => this.SetProperty(ref this._value, value); } /// diff --git a/Stylet/PropertyChangedBase.cs b/Stylet/PropertyChangedBase.cs deleted file mode 100644 index 3358d23..0000000 --- a/Stylet/PropertyChangedBase.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq.Expressions; -using System.Runtime.CompilerServices; - -namespace Stylet; - -/// -/// Base class for things which can raise PropertyChanged events -/// -public abstract class PropertyChangedBase : INotifyPropertyChanged, INotifyPropertyChangedDispatcher -{ - private Action _propertyChangedDispatcher = Execute.DefaultPropertyChangedDispatcher; - - /// - /// Gets or sets the dispatcher to use to dispatch PropertyChanged events. Defaults to Execute.DefaultPropertyChangedDispatcher - /// - [System.Xml.Serialization.XmlIgnore] -#if !NETFRAMEWORK - [System.Text.Json.Serialization.JsonIgnore] -#endif - public virtual Action PropertyChangedDispatcher - { - get => this._propertyChangedDispatcher; - set => this._propertyChangedDispatcher = value; - } - - /// - /// Occurs when a property value changes - /// - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Refresh all properties - /// - public void Refresh() - { - this.NotifyOfPropertyChange(string.Empty); - } - - /// - /// Raise a PropertyChanged notification from the property in the given expression, e.g. NotifyOfPropertyChange(() => this.Property) - /// - /// Type of property being notified - /// Expression describing the property to raise a PropertyChanged notification for - protected virtual void NotifyOfPropertyChange(Expression> property) - { - this.OnPropertyChanged(property.NameForProperty()); - } - - /// - /// Raise a PropertyChanged notification from the property with the given name - /// - /// Name of the property to raise a PropertyChanged notification for. Defaults to the calling property - protected virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = "") - { - this.OnPropertyChanged(propertyName); - } - - /// - /// Fires the PropertyChanged notification. - /// - /// Specially named so that Fody.PropertyChanged calls it - /// Name of the property to raise the notification for - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual void OnPropertyChanged(string propertyName) - { - if (this.PropertyChanged != null) - { - this.PropertyChangedDispatcher(() => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))); - } - } - - /// - /// Takes, by reference, a field, and its new value. If field != value, will set field = value and raise a PropertyChanged notification - /// - /// Type of field being set and notified - /// Field to assign - /// Value to assign to the field, if it differs - /// Name of the property to notify for. Defaults to the calling property - /// True if field != value and a notification was raised; false otherwise - protected virtual bool SetAndNotify(ref T field, T value, [CallerMemberName] string propertyName = "") - { - if (!EqualityComparer.Default.Equals(field, value)) - { - field = value; - this.NotifyOfPropertyChange(propertyName: propertyName); - return true; - } - else - { - return false; - } - } -} diff --git a/Stylet/Screen.cs b/Stylet/Screen.cs index ca32f4a..e5ab82b 100644 --- a/Stylet/Screen.cs +++ b/Stylet/Screen.cs @@ -3,26 +3,21 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using System.Windows; +using CommunityToolkit.Mvvm.ComponentModel; namespace Stylet; /// /// Implementation of IScreen. Useful as a base class for your ViewModels /// -public class Screen : ValidatingModelBase, IScreen +public class Screen : ObservableValidator, IScreen { private readonly ILogger logger; /// /// Initialises a new instance of the class, without setting up a validator /// - public Screen() : this(null) { } - - /// - /// Initialises a new instance of the class, which can validate properties using the given validator - /// - /// Validator to use - public Screen(IModelValidator validator) : base(validator) + public Screen() { Type type = this.GetType(); this.DisplayName = type.FullName; @@ -40,7 +35,7 @@ public Screen(IModelValidator validator) : base(validator) public string DisplayName { get => this._displayName; - set => this.SetAndNotify(ref this._displayName, value); + set => this.SetProperty(ref this._displayName, value); } #endregion @@ -67,14 +62,14 @@ public virtual ScreenState ScreenState get => this._screenState; protected set { - if (this.SetAndNotify(ref this._screenState, value)) + if (this.SetProperty(ref this._screenState, value)) { // Temporary, until we remove 'State' #pragma warning disable CS0618 // Type or member is obsolete - this.NotifyOfPropertyChange(nameof(this.State)); + this.OnPropertyChanged(nameof(this.State)); #pragma warning restore CS0618 // Type or member is obsolete } - this.NotifyOfPropertyChange(nameof(this.IsActive)); + this.OnPropertyChanged(nameof(this.IsActive)); } } @@ -254,7 +249,7 @@ protected virtual void OnViewLoaded() { } public object Parent { get => this._parent; - set => this.SetAndNotify(ref this._parent, value); + set => this.SetProperty(ref this._parent, value); } #endregion diff --git a/Stylet/Stylet.csproj b/Stylet/Stylet.csproj index 2bb19ec..71d7869 100644 --- a/Stylet/Stylet.csproj +++ b/Stylet/Stylet.csproj @@ -2,7 +2,7 @@ Library - net452;netcoreapp3.0;netcoreapp3.1;net5.0-windows + net462;net6.0-windows;net8.0-windows false 10 true @@ -30,6 +30,10 @@ portable + + + + diff --git a/Stylet/ValidatingModelBase.cs b/Stylet/ValidatingModelBase.cs deleted file mode 100644 index 3b82781..0000000 --- a/Stylet/ValidatingModelBase.cs +++ /dev/null @@ -1,363 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Linq.Expressions; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace Stylet; - -/// -/// Base for ViewModels which require property validation -/// -public class ValidatingModelBase : PropertyChangedBase, INotifyDataErrorInfo -{ - /// - /// Occurs when the validation errors have changed for a property or for the entire entity. - /// - public event EventHandler ErrorsChanged; - - private readonly SemaphoreSlim propertyErrorsLock = new(1, 1); - private readonly Dictionary propertyErrors = new(); - private IModelValidator _validator; - - /// - /// Gets or sets the IModelValidator to use to validate properties. You're expected to write your own, using your favourite validation library - /// - protected virtual IModelValidator Validator - { - get => this._validator; - set - { - this._validator = value; - if (this._validator != null) - this._validator.Initialize(this); - } - } - - /// - /// Gets or sets a value indicating whether to run validation for a property automatically every time that property changes - /// - protected bool AutoValidate { get; set; } - - /// - /// Initialises a new instance of the class, without using an - /// - public ValidatingModelBase() - { - this.AutoValidate = true; - } - - /// - /// Initialises a new instance of the class, using the specifies - /// - /// Validator adapter to use to perform validations - public ValidatingModelBase(IModelValidator validator) : this() - { - // Can't set this.validator, as it's virtual, and FxCop complains - this._validator = validator; - if (this._validator != null) - this._validator.Initialize(this); - } - - private bool ErrorsEqual(string[] e1, string[] e2) - { - if (e1 == null && e2 == null) - return true; - if (e1 == null || e2 == null) - return false; - return e1.SequenceEqual(e2); - } - - /// - /// Validate all properties, synchronously - /// - /// True if all properties validated successfully - protected bool Validate() - { - try - { - return this.ValidateAsync().Result; - } - catch (AggregateException e) - { - // We're only ever going to get one InnerException here - let's be nice and unwrap it - throw e.InnerException; - } - } - - /// - /// Validate all properties. - /// - /// True if all properties validated successfully - /// If you override this, you MUST fire ErrorsChanged as appropriate, and call ValidationStateChanged - protected virtual async Task ValidateAsync() - { - if (this.Validator == null) - throw new InvalidOperationException("Can't run validation if a validator hasn't been set"); - - // We need the ConfigureAwait(false), as we might be called synchronously - // However this means that the stuff after the await can be run in parallel on multiple threads - // Therefore, we need the lock - // However, we can't raise PropertyChanged events from within the lock, otherwise deadlock - Dictionary> results = await this.Validator.ValidateAllPropertiesAsync().ConfigureAwait(false); - if (results == null) - results = new Dictionary>(); - - var changedProperties = new List(); - await this.propertyErrorsLock.WaitAsync().ConfigureAwait(false); - try - { - foreach (KeyValuePair> kvp in results) - { - string[] newErrors = kvp.Value?.ToArray(); - if (!this.propertyErrors.ContainsKey(kvp.Key)) - this.propertyErrors[kvp.Key] = newErrors; - else if (this.ErrorsEqual(this.propertyErrors[kvp.Key], newErrors)) - continue; - else - this.propertyErrors[kvp.Key] = newErrors; - changedProperties.Add(kvp.Key); - } - - // If they haven't included a key in their validation results, that counts as no validation error - foreach (string removedKey in this.propertyErrors.Keys.Except(results.Keys).ToArray()) - { - this.propertyErrors[removedKey] = null; - changedProperties.Add(removedKey); - } - } - finally - { - this.propertyErrorsLock.Release(); - } - - if (changedProperties.Count > 0) - this.OnValidationStateChanged(changedProperties); - - return !this.HasErrors; - } - - /// - /// Record a property error (or clear an error on a property). You can use this independently of the validation done by - /// - /// Name of the property to change the errors for (or to change the errors for the whole model) - /// The new errors, or null to clear errors for this property - protected virtual void RecordPropertyError(Expression> property, string[] errors) - { - this.RecordPropertyError(property.NameForProperty(), errors); - } - - /// - /// Record a property error (or clear an error on a property). You can use this independently of the validation done by - /// - /// Name of the property to change the errors for (or to change the errors for the whole model) - /// The new errors, or null to clear errors for this property - protected virtual void RecordPropertyError(string propertyName, string[] errors) - { - if (propertyName == null) - propertyName = string.Empty; - - bool changed = false; - this.propertyErrorsLock.Wait(); - try - { - string[] existingErrors; - if (!this.propertyErrors.TryGetValue(propertyName, out existingErrors) || !this.ErrorsEqual(errors, existingErrors)) - { - this.propertyErrors[propertyName] = errors; - changed = true; - } - } - finally - { - this.propertyErrorsLock.Release(); - } - - if (changed) - { - this.OnValidationStateChanged(new[] { propertyName }); - } - } - - /// - /// Clear all property errors - /// - protected virtual void ClearAllPropertyErrors() - { - List changedProperties; - - this.propertyErrorsLock.Wait(); - try - { - changedProperties = this.propertyErrors.Keys.ToList(); - this.propertyErrors.Clear(); - } - finally - { - this.propertyErrorsLock.Release(); - } - - if (changedProperties.Count > 0) - { - this.OnValidationStateChanged(changedProperties); - } - } - - /// - /// Validate a single property synchronously, by name - /// - /// Type of property to validate - /// Expression describing the property to validate - /// True if the property validated successfully - protected virtual bool ValidateProperty(Expression> property) - { - return this.ValidateProperty(property.NameForProperty()); - } - - /// - /// Validate a single property asynchronously, by name - /// - /// Type ofproperty to validate - /// Expression describing the property to validate - /// True if the property validated successfully - protected virtual Task ValidatePropertyAsync(Expression> property) - { - return this.ValidatePropertyAsync(property.NameForProperty()); - } - - /// - /// Validate a single property synchronously, by name. - /// - /// Property to validate - /// True if the property validated successfully - protected bool ValidateProperty([CallerMemberName] string propertyName = null) - { - try - { - return this.ValidatePropertyAsync(propertyName).Result; - } - catch (AggregateException e) - { - // We're only ever going to get one InnerException here. Let's be nice and unwrap it - throw e.InnerException; - } - } - - /// - /// Validate a single property asynchronously, by name. - /// - /// Property to validate. Validates the entire model if null or - /// True if the property validated successfully - /// If you override this, you MUST fire ErrorsChanged and call OnValidationStateChanged() if appropriate - protected virtual async Task ValidatePropertyAsync([CallerMemberName] string propertyName = null) - { - if (this.Validator == null) - throw new InvalidOperationException("Can't run validation if a validator hasn't been set"); - - if (propertyName == null) - propertyName = string.Empty; - - // To allow synchronous calling of this method, we need to resume on the ThreadPool. - // Therefore, we might resume on any thread, hence the need for a lock - IEnumerable newErrorsRaw = await this.Validator.ValidatePropertyAsync(propertyName).ConfigureAwait(false); - string[] newErrors = newErrorsRaw?.ToArray(); - bool propertyErrorsChanged = false; - - await this.propertyErrorsLock.WaitAsync().ConfigureAwait(false); - try - { - if (!this.propertyErrors.ContainsKey(propertyName)) - this.propertyErrors.Add(propertyName, null); - - if (!this.ErrorsEqual(this.propertyErrors[propertyName], newErrors)) - { - this.propertyErrors[propertyName] = newErrors; - propertyErrorsChanged = true; - } - } - finally - { - this.propertyErrorsLock.Release(); - } - - if (propertyErrorsChanged) - this.OnValidationStateChanged(new[] { propertyName }); - - return newErrors == null || newErrors.Length == 0; - } - - /// - /// Raise a PropertyChanged notification for the named property, and validate that property if this.validation is set and this.autoValidate is true - /// - /// Name of the property which has changed - [EditorBrowsable(EditorBrowsableState.Never)] - protected override async void OnPropertyChanged(string propertyName) - { - base.OnPropertyChanged(propertyName); - - // Save ourselves a little bit of work every time HasErrors is fired as the result of - // the validation results changing. - if (this.Validator != null && this.AutoValidate && propertyName != "HasErrors") - await this.ValidatePropertyAsync(propertyName); - } - - /// - /// Called whenever the error state of any properties changes. Calls NotifyOfPropertyChange("HasErrors") by default - /// - /// List of property names which have changed validation state - protected virtual void OnValidationStateChanged(IEnumerable changedProperties) - { - this.NotifyOfPropertyChange(nameof(this.HasErrors)); - foreach (string property in changedProperties) - { - this.RaiseErrorsChanged(property); - } - } - - /// - /// Raise the ErrorsChanged event for a given property - /// - /// Property to raise the ErrorsChanged event for - protected virtual void RaiseErrorsChanged(string propertyName) - { - EventHandler handler = this.ErrorsChanged; - if (handler != null) - this.PropertyChangedDispatcher(() => handler(this, new DataErrorsChangedEventArgs(propertyName))); - } - - /// - /// Gets the validation errors for a specified property or for the entire entity. - /// - /// The name of the property to retrieve validation errors for; or null or System.String.Empty, to retrieve entity-level errors. - /// The validation errors for the property or entity. - public virtual IEnumerable GetErrors(string propertyName) - { - string[] errors; - - if (propertyName == null) - propertyName = string.Empty; - - // We'll just have to wait synchronously for this. Oh well. The lock shouldn't be long. - // Everything that awaits uses ConfigureAwait(false), so we shouldn't deadlock if someone calls this on the main thread - this.propertyErrorsLock.Wait(); - try - { - this.propertyErrors.TryGetValue(propertyName, out errors); - } - finally - { - this.propertyErrorsLock.Release(); - } - - return errors; - } - - /// - /// Gets a value indicating whether the entity has validation errors. - /// - public virtual bool HasErrors => this.propertyErrors.Values.Any(x => x != null && x.Length > 0); -} diff --git a/StyletIntegrationTests/ShellViewModel.cs b/StyletIntegrationTests/ShellViewModel.cs index f5c62e7..9cffa47 100644 --- a/StyletIntegrationTests/ShellViewModel.cs +++ b/StyletIntegrationTests/ShellViewModel.cs @@ -25,7 +25,7 @@ public ShellViewModel(IWindowManager windowManager) public bool? ShowDialogAndDialogResultDialogResult { get => this._showDialogAndDialogResultDialogResult; - set => this.SetAndNotify(ref this._showDialogAndDialogResultDialogResult, value); + set => this.SetProperty(ref this._showDialogAndDialogResultDialogResult, value); } public void ShowDialogAndDialogResult() diff --git a/StyletIntegrationTests/StyletIntegrationTests.csproj b/StyletIntegrationTests/StyletIntegrationTests.csproj index 09c82d9..c5982cd 100644 --- a/StyletIntegrationTests/StyletIntegrationTests.csproj +++ b/StyletIntegrationTests/StyletIntegrationTests.csproj @@ -2,7 +2,7 @@ WinExe - net452;netcoreapp3.1;net5.0-windows + net462;net6.0-windows;net8.0-windows true false diff --git a/StyletUnitTests/CommandActionTests.cs b/StyletUnitTests/CommandActionTests.cs index 468f825..f1d7fba 100644 --- a/StyletUnitTests/CommandActionTests.cs +++ b/StyletUnitTests/CommandActionTests.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using CommunityToolkit.Mvvm.ComponentModel; +using NUnit.Framework; using Stylet; using Stylet.Xaml; using System; @@ -10,7 +11,7 @@ namespace StyletUnitTests; [TestFixture] public class CommandActionTests { - private class Target : PropertyChangedBase + private class Target : ObservableObject { public bool DoSomethingCalled; public void DoSomething() @@ -22,7 +23,7 @@ public void DoSomething() public bool CanDoSomethingWithGuard { get => this._canDoSomethingWithGuard; - set => this.SetAndNotify(ref this._canDoSomethingWithGuard, value); + set => this.SetProperty(ref this._canDoSomethingWithGuard, value); } public bool DoSomethingWithGuardCalled; diff --git a/StyletUnitTests/ConductorAllActiveTests.cs b/StyletUnitTests/ConductorAllActiveTests.cs index 1621589..76af2bd 100644 --- a/StyletUnitTests/ConductorAllActiveTests.cs +++ b/StyletUnitTests/ConductorAllActiveTests.cs @@ -12,7 +12,7 @@ public class ConductorAllActiveTests public interface IMyScreen : IScreen, IDisposable { } - private class MyConductor : Conductor.Collection.AllActive + internal class MyConductor : Conductor.Collection.AllActive { public bool CanCloseValue = true; public override async Task CanCloseAsync() diff --git a/StyletUnitTests/ConductorNavigatingTests.cs b/StyletUnitTests/ConductorNavigatingTests.cs index 1670e9c..666cb46 100644 --- a/StyletUnitTests/ConductorNavigatingTests.cs +++ b/StyletUnitTests/ConductorNavigatingTests.cs @@ -13,7 +13,7 @@ public class ConductorNavigatingTests public interface IMyScreen : IScreen, IDisposable { } - private class MyConductor : Conductor.StackNavigation + internal class MyConductor : Conductor.StackNavigation { public bool CanCloseValue = true; public override async Task CanCloseAsync() diff --git a/StyletUnitTests/ConductorOneActiveTests.cs b/StyletUnitTests/ConductorOneActiveTests.cs index 72b2feb..b340415 100644 --- a/StyletUnitTests/ConductorOneActiveTests.cs +++ b/StyletUnitTests/ConductorOneActiveTests.cs @@ -12,7 +12,7 @@ public class ConductorOneActiveTests public interface IMyScreen : IScreen, IDisposable { } - private class MyConductor : Conductor.Collection.OneActive + internal class MyConductor : Conductor.Collection.OneActive { public bool CanCloseValue = true; public override async Task CanCloseAsync() diff --git a/StyletUnitTests/ConductorTests.cs b/StyletUnitTests/ConductorTests.cs index fe4f766..b68feca 100644 --- a/StyletUnitTests/ConductorTests.cs +++ b/StyletUnitTests/ConductorTests.cs @@ -12,7 +12,7 @@ public class ConductorTests public interface IMyScreen : IScreen, IDisposable { } - private class MyConductor : Conductor + internal class MyConductor : Conductor { public bool CanCloseValue = true; public override async Task CanCloseAsync() diff --git a/StyletUnitTests/LabelledValueTests.cs b/StyletUnitTests/LabelledValueTests.cs index d5adf44..f66a6f1 100644 --- a/StyletUnitTests/LabelledValueTests.cs +++ b/StyletUnitTests/LabelledValueTests.cs @@ -20,7 +20,6 @@ public void SettingLabelAndValueRaisePropertyChangedNotifications() { var lbv = new LabelledValue(); - lbv.PropertyChangedDispatcher = a => a(); var changedProperties = new List(); lbv.PropertyChanged += (o, e) => changedProperties.Add(e.PropertyName); diff --git a/StyletUnitTests/MessageBoxTests.cs b/StyletUnitTests/MessageBoxTests.cs index 12c818c..610f585 100644 --- a/StyletUnitTests/MessageBoxTests.cs +++ b/StyletUnitTests/MessageBoxTests.cs @@ -12,7 +12,7 @@ namespace StyletUnitTests; [TestFixture] public class MessageBoxTests { - private class MyMessageBoxViewModel : MessageBoxViewModel + internal class MyMessageBoxViewModel : MessageBoxViewModel { public new void OnViewLoaded() { diff --git a/StyletUnitTests/PropertyChangedBaseTests.cs b/StyletUnitTests/PropertyChangedBaseTests.cs deleted file mode 100644 index 075fae0..0000000 --- a/StyletUnitTests/PropertyChangedBaseTests.cs +++ /dev/null @@ -1,124 +0,0 @@ -using NUnit.Framework; -using Stylet; -using System; - -namespace StyletUnitTests; - -[TestFixture] -public class PropertyChangedBaseTests -{ - private class PropertyChanged : PropertyChangedBase - { - public int IntProperty { get; set; } - public string StringProperty - { - set => this.NotifyOfPropertyChange(); - } - private double _doubleProperty; - public double DoubleProperty - { - get => this._doubleProperty; - set => this.SetAndNotify(ref this._doubleProperty, value); - } - public void RaiseIntPropertyChangedWithExpression() - { - this.NotifyOfPropertyChange(() => this.IntProperty); - } - public void RaiseIntPropertyChangedWithString() - { - this.NotifyOfPropertyChange(nameof(this.IntProperty)); - } - } - - [Test] - public void RefreshRaisesPropertyChangedWithEmptyString() - { - var pc = new PropertyChanged(); - string changedProperty = null; - pc.PropertyChanged += (o, e) => changedProperty = e.PropertyName; - pc.Refresh(); - Assert.AreEqual(string.Empty, changedProperty); - } - - [Test] - public void NotifyOfPropertyChangedWithExpressionRaises() - { - var pc = new PropertyChanged(); - string changedProperty = null; - pc.PropertyChanged += (o, e) => changedProperty = e.PropertyName; - pc.RaiseIntPropertyChangedWithExpression(); - Assert.AreEqual("IntProperty", changedProperty); - } - - [Test] - public void NotifyOfPropertyChangedWithStringRaises() - { - var pc = new PropertyChanged(); - string changedProperty = null; - pc.PropertyChanged += (o, e) => changedProperty = e.PropertyName; - pc.RaiseIntPropertyChangedWithString(); - Assert.AreEqual("IntProperty", changedProperty); - } - - [Test] - public void NotifyOfPropertyChangedWithCallerMemberName() - { - var pc = new PropertyChanged(); - string changedProperty = null; - pc.PropertyChanged += (o, e) => changedProperty = e.PropertyName; - pc.StringProperty = "hello"; - Assert.AreEqual("StringProperty", changedProperty); - } - - [Test] - public void UsesDispatcher() - { - var pc = new PropertyChanged(); - string changedProperty = null; - pc.PropertyChanged += (o, e) => changedProperty = e.PropertyName; - - Action action = null; - pc.PropertyChangedDispatcher = a => action = a; - - pc.RaiseIntPropertyChangedWithExpression(); - Assert.IsNull(changedProperty); - Assert.IsNotNull(action); - - action(); - Assert.AreEqual("IntProperty", changedProperty); - } - - [Test] - public void UsesStaticDispatcherByDefault() - { - Action action = null; - Action oldDispatcher = Execute.DefaultPropertyChangedDispatcher; - Execute.DefaultPropertyChangedDispatcher = a => action = a; - - var pc = new PropertyChanged(); - string changedProperty = null; - pc.PropertyChanged += (o, e) => changedProperty = e.PropertyName; - - pc.RaiseIntPropertyChangedWithExpression(); - Assert.IsNull(changedProperty); - Assert.IsNotNull(action); - - action(); - Assert.AreEqual("IntProperty", changedProperty); - - Execute.DefaultPropertyChangedDispatcher = oldDispatcher; - } - - [Test] - public void SetAndNotifyWorks() - { - var pc = new PropertyChanged(); - string changedProperty = null; - pc.PropertyChanged += (o, e) => changedProperty = e.PropertyName; - - pc.DoubleProperty = 5; - - Assert.AreEqual("DoubleProperty", changedProperty); - Assert.AreEqual(5, pc.DoubleProperty); - } -} diff --git a/StyletUnitTests/PropertyChangedExtensionsTests.cs b/StyletUnitTests/PropertyChangedExtensionsTests.cs index aa62f25..7ca0ab0 100644 --- a/StyletUnitTests/PropertyChangedExtensionsTests.cs +++ b/StyletUnitTests/PropertyChangedExtensionsTests.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using CommunityToolkit.Mvvm.ComponentModel; +using NUnit.Framework; using Stylet; using System; @@ -7,25 +8,25 @@ namespace StyletUnitTests; [TestFixture] public class PropertyChangedExtensionsTests { - private class NotifyingClass : PropertyChangedBase + private class NotifyingClass : ObservableObject { private string _foo; public string Foo { get => this._foo; - set => this.SetAndNotify(ref this._foo, value); + set => this.SetProperty(ref this._foo, value); } private string _bar; public string Bar { get => this._bar; - set => this.SetAndNotify(ref this._bar, value); + set => this.SetProperty(ref this._bar, value); } public void NotifyAll() { - this.NotifyOfPropertyChange(string.Empty); + this.OnPropertyChanged(string.Empty); } } diff --git a/StyletUnitTests/ScreenTests.cs b/StyletUnitTests/ScreenTests.cs index 27428f0..a155e00 100644 --- a/StyletUnitTests/ScreenTests.cs +++ b/StyletUnitTests/ScreenTests.cs @@ -11,16 +11,9 @@ namespace StyletUnitTests; [TestFixture] public class ScreenTests { - private class MyScreen : Screen + internal class MyScreen : Screen { - public new IModelValidator Validator - { - get => base.Validator; - set => base.Validator = value; - } - public MyScreen() { } - public MyScreen(IModelValidator validator) : base(validator) { } public void Reset() { @@ -418,14 +411,6 @@ public void TryCloseCallsParentCloseItemPassingDialogResult() #pragma warning restore 618 - [Test] - public void PassesValidatorAdapter() - { - var adapter = new Mock(); - var screen = new MyScreen(adapter.Object); - Assert.AreEqual(adapter.Object, screen.Validator); - } - [Test] public void InitialActivateFiredWhenComingFromDeactivated() { diff --git a/StyletUnitTests/StyletUnitTests.csproj b/StyletUnitTests/StyletUnitTests.csproj index 0204dca..cf6088b 100644 --- a/StyletUnitTests/StyletUnitTests.csproj +++ b/StyletUnitTests/StyletUnitTests.csproj @@ -1,7 +1,7 @@  - net472;net5.0-windows + net462;net6.0-windows;net8.0-windows true false diff --git a/StyletUnitTests/ValidatingModelBaseTests.cs b/StyletUnitTests/ValidatingModelBaseTests.cs deleted file mode 100644 index aa8a016..0000000 --- a/StyletUnitTests/ValidatingModelBaseTests.cs +++ /dev/null @@ -1,372 +0,0 @@ -using Moq; -using NUnit.Framework; -using Stylet; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace StyletUnitTests; - -[TestFixture] -public class ValidatingModelBaseTests -{ - private class MyModel : ValidatingModelBase - { - public MyModel() : base() { } - public MyModel(IModelValidator validator) : base(validator) { } - - private int _intProperty; - public int IntProperty - { - get => this._intProperty; - set => this.SetAndNotify(ref this._intProperty, value); - } - - public new bool AutoValidate - { - get => base.AutoValidate; - set => base.AutoValidate = value; - } - - public new IModelValidator Validator - { - get => base.Validator; - set => base.Validator = value; - } - - public new bool Validate() - { - return base.Validate(); - } - - public new Task ValidateAsync() - { - return base.ValidateAsync(); - } - - public new bool ValidateProperty(string propertyName) - { - return base.ValidateProperty(propertyName); - } - - public new bool ValidateProperty(Expression> property) - { - return base.ValidateProperty(property); - } - - public new Task ValidatePropertyAsync(string propertyName) - { - return base.ValidatePropertyAsync(propertyName); - } - - public new Task ValidatePropertyAsync(Expression> property) - { - return base.ValidatePropertyAsync(property); - } - } - - private Mock validator; - private MyModel model; - - [SetUp] - public void SetUp() - { - this.validator = new Mock(); - this.model = new MyModel(); - this.model.Validator = this.validator.Object; - } - - [Test] - public void PropertySetsAndInitialisesModelValidator() - { - this.validator.Verify(x => x.Initialize(this.model)); - Assert.AreEqual(this.validator.Object, this.model.Validator); - } - - [Test] - public void ConstructorSetsAndInitialisesModelValidator() - { - this.validator.Verify(x => x.Initialize(this.model)); - Assert.AreEqual(this.validator.Object, this.model.Validator); - } - - [Test] - public void ThrowsIfAskedToValidateAndNoValidatorSet() - { - this.model.Validator = null; - Assert.Throws(() => this.model.Validate()); - Assert.Throws(() => this.model.ValidateProperty("test")); - } - - [Test] - public void ValidateCallsAdapterValidate() - { - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>() { { "property", new[] { "error1", "error2" } } }).Verifiable(); - this.model.Validate(); - - this.validator.Verify(); - } - - [Test] - public void ValidateAsyncCallsAdapterValidate() - { - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>()).Verifiable(); - this.model.ValidateAsync().Wait(); - - this.validator.Verify(); - } - - [Test] - public void ValidatePropertyByNameCallsAdapterValidate() - { - this.validator.Setup(x => x.ValidatePropertyAsync("test")).ReturnsAsync(Enumerable.Empty()).Verifiable(); - this.model.ValidateProperty("test"); - - this.validator.Verify(); - } - - [Test] - public void ValidatePropertyAsyncByNameCallsAdapterValidate() - { - this.validator.Setup(x => x.ValidatePropertyAsync("test")).ReturnsAsync(Enumerable.Empty()).Verifiable(); - this.model.ValidatePropertyAsync("test").Wait(); - - this.validator.Verify(); - } - - [Test] - public void ValidatePropertyAsyncWitNullCallsAdapterValidatePropertyWithEmptyString() - { - this.validator.Setup(x => x.ValidatePropertyAsync(string.Empty)).ReturnsAsync(Enumerable.Empty()).Verifiable(); - this.model.ValidatePropertyAsync(string.Empty).Wait(); - - this.validator.Verify(); - } - - - [Test] - public void ValidatePropertyAsyncWithEmptyStringCallsAdapterValidatePropertyWithEmptyString() - { - this.validator.Setup(x => x.ValidatePropertyAsync(string.Empty)).ReturnsAsync(Enumerable.Empty()).Verifiable(); - this.model.ValidatePropertyAsync(string.Empty).Wait(); - - this.validator.Verify(); - } - [Test] - public void ValidatePropertyByExpressionCallsAdapterValidate() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(Enumerable.Empty()).Verifiable(); - this.model.ValidateProperty(() => this.model.IntProperty); - - this.validator.Verify(); - } - - [Test] - public void ValidatePropertAsyncByExpressionCallsAdapterValidate() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(Enumerable.Empty()).Verifiable(); - this.model.ValidatePropertyAsync(() => this.model.IntProperty).Wait(); - - this.validator.Verify(); - } - - [Test] - public void ValidatePropertyReturnsTrueIfValidationPassed() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync((IEnumerable)null); - bool result = this.model.ValidateProperty("IntProperty"); - Assert.True(result); - - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new string[0]); - result = this.model.ValidateProperty("IntProperty"); - Assert.True(result); - } - - [Test] - public void ValidatePropertyReturnsFalseIfValidationFailed() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new[] { "error" }); - bool result = this.model.ValidateProperty("IntProperty"); - Assert.False(result); - } - - [Test] - public void ValidateReturnsTrueIfValidationPassed() - { - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>() - { - { "IntProperty", null } - }); - bool result = this.model.Validate(); - Assert.True(result); - - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>() - { - { "IntProperty", new string[0] } - }); - result = this.model.Validate(); - Assert.True(result); - } - - [Test] - public void ValidateReturnsFalseIfValidationFailed() - { - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>() - { - { "IntProperty", new[] { "error" } } - }); - bool result = this.model.Validate(); - Assert.False(result); - } - - [Test] - public void EventRaisedAndHasErrorsChangedIfErrorWasNullAndNowIsNot() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync((IEnumerable)null); - this.model.ValidateProperty("IntProperty"); - - string changedProperty = null; - this.model.ErrorsChanged += (o, e) => changedProperty = e.PropertyName; - bool hasErrorsRaised = false; - this.model.PropertyChanged += (o, e) => { if (e.PropertyName == "HasErrors") hasErrorsRaised = true; }; - - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new[] { "error" }); - this.model.ValidateProperty("IntProperty"); - - Assert.AreEqual("IntProperty", changedProperty); - Assert.True(hasErrorsRaised); - } - - [Test] - public void EventRaisedAndHasErrorsChangedIfErrorWasEmptyArrayAndNowIsNot() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new string[0]); - this.model.ValidateProperty("IntProperty"); - - string changedProperty = null; - this.model.ErrorsChanged += (o, e) => changedProperty = e.PropertyName; - bool hasErrorsRaised = false; - this.model.PropertyChanged += (o, e) => { if (e.PropertyName == "HasErrors") hasErrorsRaised = true; }; - - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new[] { "error" }); - this.model.ValidateProperty("IntProperty"); - - Assert.AreEqual("IntProperty", changedProperty); - Assert.True(hasErrorsRaised); - } - - [Test] - public void EventRaisedAndHasErrorsChangedIfErrorWasSetAndIsNowNull() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new[] { "error" }); - this.model.ValidateProperty("IntProperty"); - - string changedProperty = null; - this.model.ErrorsChanged += (o, e) => changedProperty = e.PropertyName; - bool hasErrorsRaised = false; - this.model.PropertyChanged += (o, e) => { if (e.PropertyName == "HasErrors") hasErrorsRaised = true; }; - - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync((IEnumerable)null); - this.model.ValidateProperty("IntProperty"); - - Assert.AreEqual("IntProperty", changedProperty); - Assert.True(hasErrorsRaised); - } - - [Test] - public void EventRaisedAndHasErrorsChangedIfErrorWasSetAndIsNowEmptyArray() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new[] { "error" }); - this.model.ValidateProperty("IntProperty"); - - string changedProperty = null; - this.model.ErrorsChanged += (o, e) => changedProperty = e.PropertyName; - bool hasErrorsRaised = false; - this.model.PropertyChanged += (o, e) => { if (e.PropertyName == "HasErrors") hasErrorsRaised = true; }; - - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new string[0]); - this.model.ValidateProperty("IntProperty"); - - Assert.AreEqual("IntProperty", changedProperty); - Assert.True(hasErrorsRaised); - } - - [Test] - public void EventRaisedAndHasErrorsChangedIfValidateAllAndErrorsChange() - { - // Set up some initial errors - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>() - { - { "IntProperty", new[] { "error" } }, - { "OtherProperty", null }, - { "OtherOtherProperty", new string[0] }, - { "PropertyThatWillDisappear", new[] { "error" } }, - }); - this.model.Validate(); - - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>() - { - { "IntProperty", new[] { "error" } }, - { "OtherProperty", new[] { "error" } }, - { "OtherOtherProperty", new string[0] }, - { "NewOKProperty", null }, - { "NewNotOKProperty", new[] { "woo" } }, - }); - - var errors = new List(); - this.model.ErrorsChanged += (o, e) => errors.Add(e.PropertyName); - int hasErrorsChangedCount = 0; - this.model.PropertyChanged += (o, e) => { if (e.PropertyName == "HasErrors") hasErrorsChangedCount++; }; - - this.model.Validate(); - - Assert.That(errors, Is.EquivalentTo(new[] { "OtherProperty", "NewOKProperty", "NewNotOKProperty", "PropertyThatWillDisappear" })); - Assert.AreEqual(1, hasErrorsChangedCount); - } - - [Test] - public void GetErrorsReturnsNullIfNoErrorsForThatProperty() - { - System.Collections.IEnumerable errors = this.model.GetErrors("FooBar"); - Assert.Null(errors); - } - - [Test] - public void GetErrorsReturnsErrorsForProperty() - { - this.validator.Setup(x => x.ValidatePropertyAsync("IntProperty")).ReturnsAsync(new[] { "error1", "error2" }); - this.model.ValidateProperty("IntProperty"); - System.Collections.IEnumerable errors = this.model.GetErrors("IntProperty"); - Assert.That(errors, Is.EquivalentTo(new[] { "error1", "error2" })); - } - - [Test] - public void GetErrorsWithNullReturnsModelErrors() - { - this.validator.Setup(x => x.ValidateAllPropertiesAsync()).ReturnsAsync(new Dictionary>() - { - { "", new[] { "error1", "error2" } } - }); - - this.model.Validate(); - System.Collections.IEnumerable errors = this.model.GetErrors(null); - Assert.That(errors, Is.EquivalentTo(new[] { "error1", "error2" })); - } - - [Test] - public void SettingPropertyValidatesIfAutoValidateIsTrue() - { - this.model.IntProperty = 5; - this.validator.Verify(x => x.ValidatePropertyAsync("IntProperty")); - } - - [Test] - public void SettingPropertyDoesNotValidateIfAutoValidateIsFalse() - { - this.model.AutoValidate = false; - this.model.IntProperty = 5; - this.validator.Verify(x => x.ValidatePropertyAsync("IntProperty"), Times.Never); - } -}