Added Validation logics
This commit is contained in:
@@ -94,8 +94,6 @@ public class ContentDialogHandler(IDispatcher dispatcher) :
|
|||||||
deactivatable.DeactivateHandler += DeactivateHandler;
|
deactivatable.DeactivateHandler += DeactivateHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A hack to wait for the dialog to finish loading up to make it appear more responsive
|
|
||||||
await Task.Delay(250);
|
|
||||||
if (content is IActivated activated)
|
if (content is IActivated activated)
|
||||||
{
|
{
|
||||||
await activated.OnActivated();
|
await activated.OnActivated();
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ public class ComponentBuilder :
|
|||||||
services.AddTransient<IPublisher, Publisher>();
|
services.AddTransient<IPublisher, Publisher>();
|
||||||
services.AddTransient<IMediator, Mediator>();
|
services.AddTransient<IMediator, Mediator>();
|
||||||
|
|
||||||
|
services.AddTransient<IValidation, Validation>();
|
||||||
|
services.AddTransient<IValidatorCollection, ValidatorCollection>();
|
||||||
|
|
||||||
services.AddTransient<IContentFactory, ContentFactory>();
|
services.AddTransient<IContentFactory, ContentFactory>();
|
||||||
services.AddTransient<INavigation, Navigation>();
|
services.AddTransient<INavigation, Navigation>();
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ public class DefaultHostBuilder :
|
|||||||
services.AddScoped<SubscriptionCollection>();
|
services.AddScoped<SubscriptionCollection>();
|
||||||
|
|
||||||
services.AddTransient<IHandlerProvider, HandlerProvider>();
|
services.AddTransient<IHandlerProvider, HandlerProvider>();
|
||||||
services.AddTransient<ISubscriber, Subscriber>();
|
services.AddScoped<ISubscriber, Subscriber>();
|
||||||
|
|
||||||
services.AddTransient<ISubscriber, Subscriber>();
|
|
||||||
|
|
||||||
services.AddTransient<IPublisher, Publisher>();
|
services.AddTransient<IPublisher, Publisher>();
|
||||||
services.AddTransient<IMediator, Mediator>();
|
services.AddTransient<IMediator, Mediator>();
|
||||||
|
|
||||||
|
services.AddTransient<IValidation, Validation>();
|
||||||
|
services.AddTransient<IValidatorCollection, ValidatorCollection>();
|
||||||
|
|
||||||
services.AddScoped<IProxyService<IPublisher>>(provider =>
|
services.AddScoped<IProxyService<IPublisher>>(provider =>
|
||||||
new ProxyService<IPublisher>(provider.GetRequiredService<IPublisher>()));
|
new ProxyService<IPublisher>(provider.GetRequiredService<IPublisher>()));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
|
public interface IValidation :
|
||||||
|
INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
ValidationErrorCollection Errors { get; }
|
||||||
|
|
||||||
|
bool HasErrors { get; }
|
||||||
|
|
||||||
|
void Add<TProperty>(Expression<Func<TProperty>> property,
|
||||||
|
ValidationRule[] rules,
|
||||||
|
ValidationTrigger trigger = ValidationTrigger.Deferred);
|
||||||
|
|
||||||
|
bool Validate();
|
||||||
|
|
||||||
|
bool Validate(string name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
|
public record Validate
|
||||||
|
{
|
||||||
|
public static ValidateEventArgs<TValue> As<TValue>(TValue value) =>
|
||||||
|
new(value);
|
||||||
|
|
||||||
|
public static ValidateEventArgs<TValue> As<TValue>() where TValue : new() =>
|
||||||
|
new(new TValue());
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
|
public record ValidateEventArgs<TValue>(TValue Value);
|
||||||
@@ -1,10 +1,89 @@
|
|||||||
namespace Toolkit.Foundation;
|
using System.ComponentModel;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
public record Validation
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
|
public class Validation(IValidatorCollection validators) :
|
||||||
|
IValidation
|
||||||
{
|
{
|
||||||
public static ValidationEventArgs<TValue> As<TValue>(TValue value) =>
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
new(value);
|
|
||||||
|
|
||||||
public static ValidationEventArgs<TValue> As<TValue>() where TValue : new() =>
|
public ValidationErrorCollection Errors { get; } = [];
|
||||||
new(new TValue());
|
|
||||||
|
public bool HasErrors =>
|
||||||
|
Errors.Count > 0;
|
||||||
|
|
||||||
|
internal IValidatorCollection Validators { get; } = validators;
|
||||||
|
|
||||||
|
public void Add<TProperty>(Expression<Func<TProperty>> property,
|
||||||
|
ValidationRule[] rules,
|
||||||
|
ValidationTrigger trigger = ValidationTrigger.Deferred)
|
||||||
|
{
|
||||||
|
string? name = GetPropertyName(property);
|
||||||
|
Validators.Add(name, new Validator(name, rules));
|
||||||
|
|
||||||
|
if (trigger is ValidationTrigger.Immediate)
|
||||||
|
{
|
||||||
|
Validate(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnPropertyChanged(string propertyName,
|
||||||
|
object? before, object? after)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate(string name)
|
||||||
|
{
|
||||||
|
if (Errors.ContainsKey(name))
|
||||||
|
{
|
||||||
|
Errors.Remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Validators.TryGet(name, out Validator? validator))
|
||||||
|
{
|
||||||
|
if (validator is not null)
|
||||||
|
{
|
||||||
|
if (!validator.TryValidate(out string? message))
|
||||||
|
{
|
||||||
|
Errors[name] = message ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(Errors), null, null);
|
||||||
|
OnPropertyChanged(nameof(HasErrors), null, null);
|
||||||
|
|
||||||
|
return !HasErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate()
|
||||||
|
{
|
||||||
|
Errors.Clear();
|
||||||
|
foreach (Validator? validator in Validators)
|
||||||
|
{
|
||||||
|
if (validator.PropertyName is string name)
|
||||||
|
{
|
||||||
|
if (!validator.TryValidate(out string? message))
|
||||||
|
{
|
||||||
|
Errors[name] = message ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(Errors), null, null);
|
||||||
|
OnPropertyChanged(nameof(HasErrors), null, null);
|
||||||
|
|
||||||
|
return !HasErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetPropertyName<T>(Expression<Func<T>> predicate)
|
||||||
|
{
|
||||||
|
Expression? body = predicate.Body;
|
||||||
|
MemberExpression? memberExpression = body as MemberExpression ??
|
||||||
|
(MemberExpression)((UnaryExpression)body).Operand;
|
||||||
|
|
||||||
|
return memberExpression.Member.Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,8 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
|
|
||||||
namespace Toolkit.Foundation;
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
public class ValidationErrorCollection : IDictionary<string, string>,
|
public class ValidationErrorCollection :
|
||||||
|
IDictionary<string, string>,
|
||||||
IDictionary,
|
IDictionary,
|
||||||
INotifyCollectionChanged,
|
INotifyCollectionChanged,
|
||||||
INotifyPropertyChanged
|
INotifyPropertyChanged
|
||||||
@@ -41,11 +42,7 @@ public class ValidationErrorCollection : IDictionary<string, string>,
|
|||||||
|
|
||||||
public string this[string key]
|
public string this[string key]
|
||||||
{
|
{
|
||||||
get
|
get => items.ContainsKey(key) ? items[key] : "";
|
||||||
{
|
|
||||||
return items[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
bool replace = items.TryGetValue(key, out var old);
|
bool replace = items.TryGetValue(key, out var old);
|
||||||
@@ -54,7 +51,8 @@ public class ValidationErrorCollection : IDictionary<string, string>,
|
|||||||
if (replace)
|
if (replace)
|
||||||
{
|
{
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
|
||||||
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, new KeyValuePair<string, string>(key, value), new KeyValuePair<string, string>(key, old!)));
|
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
|
||||||
|
new KeyValuePair<string, string>(key, value), new KeyValuePair<string, string>(key, old!)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Toolkit.Foundation;
|
|
||||||
|
|
||||||
public record ValidationEventArgs<TValue>(TValue Value);
|
|
||||||
@@ -1,22 +1,27 @@
|
|||||||
namespace Toolkit.Foundation;
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
public class PropertyValidator
|
public class ValidationRule
|
||||||
{
|
{
|
||||||
public PropertyValidator(Func<bool> validation,
|
public ValidationRule(Func<bool> validation,
|
||||||
string message)
|
string message)
|
||||||
{
|
{
|
||||||
Validation = validation;
|
Validation = validation;
|
||||||
Message = new Func<string>(() => message);
|
Message = new Func<string>(() => message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropertyValidator(Func<bool> validation,
|
public ValidationRule(Func<bool> validation)
|
||||||
|
{
|
||||||
|
Validation = validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationRule(Func<bool> validation,
|
||||||
Func<string> message)
|
Func<string> message)
|
||||||
{
|
{
|
||||||
Validation = validation;
|
Validation = validation;
|
||||||
Message = message;
|
Message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<string> Message { get; }
|
public Func<string>? Message { get; }
|
||||||
|
|
||||||
public Func<bool>? Validation { get; }
|
public Func<bool>? Validation { get; }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
|
public enum ValidationTrigger
|
||||||
|
{
|
||||||
|
Deferred,
|
||||||
|
Immediate
|
||||||
|
}
|
||||||
@@ -4,48 +4,30 @@ namespace Toolkit.Foundation;
|
|||||||
|
|
||||||
public class Validator
|
public class Validator
|
||||||
{
|
{
|
||||||
private readonly Action? propertyChanged;
|
private readonly ValidationRule[] rules = [];
|
||||||
private readonly PropertyValidator? propertyValidation;
|
|
||||||
|
|
||||||
internal Validator(string propertyName,
|
public Validator(string propertyName,
|
||||||
Action propertyChanged)
|
ValidationRule[] rules)
|
||||||
{
|
{
|
||||||
PropertyName = propertyName;
|
PropertyName = propertyName;
|
||||||
this.propertyChanged = propertyChanged;
|
this.rules = rules;
|
||||||
}
|
|
||||||
|
|
||||||
internal Validator(string propertyName,
|
|
||||||
Action propertyChanged,
|
|
||||||
PropertyValidator validation)
|
|
||||||
{
|
|
||||||
PropertyName = propertyName;
|
|
||||||
|
|
||||||
this.propertyChanged = propertyChanged;
|
|
||||||
propertyValidation = validation;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Validator(string propertyName,
|
|
||||||
PropertyValidator validation)
|
|
||||||
{
|
|
||||||
PropertyName = propertyName;
|
|
||||||
propertyValidation = validation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? PropertyName { get; }
|
public string? PropertyName { get; }
|
||||||
|
|
||||||
public void Set() => propertyChanged?.Invoke();
|
|
||||||
|
|
||||||
public bool TryValidate([MaybeNull] out string message)
|
public bool TryValidate([MaybeNull] out string message)
|
||||||
{
|
{
|
||||||
message = "";
|
message = "";
|
||||||
|
|
||||||
if (propertyValidation is not null && propertyValidation.Validation?.Invoke() == false)
|
foreach (ValidationRule rule in rules)
|
||||||
{
|
{
|
||||||
message = propertyValidation.Message.Invoke();
|
if (rule.Validation?.Invoke() == false)
|
||||||
|
{
|
||||||
|
message = rule.Message?.Invoke();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
propertyChanged?.Invoke();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user