Added Validation logics
This commit is contained in:
@@ -94,8 +94,6 @@ public class ContentDialogHandler(IDispatcher dispatcher) :
|
||||
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)
|
||||
{
|
||||
await activated.OnActivated();
|
||||
|
||||
@@ -36,6 +36,9 @@ public class ComponentBuilder :
|
||||
services.AddTransient<IPublisher, Publisher>();
|
||||
services.AddTransient<IMediator, Mediator>();
|
||||
|
||||
services.AddTransient<IValidation, Validation>();
|
||||
services.AddTransient<IValidatorCollection, ValidatorCollection>();
|
||||
|
||||
services.AddTransient<IContentFactory, ContentFactory>();
|
||||
services.AddTransient<INavigation, Navigation>();
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ public class DefaultHostBuilder :
|
||||
services.AddScoped<SubscriptionCollection>();
|
||||
|
||||
services.AddTransient<IHandlerProvider, HandlerProvider>();
|
||||
services.AddTransient<ISubscriber, Subscriber>();
|
||||
|
||||
services.AddTransient<ISubscriber, Subscriber>();
|
||||
|
||||
services.AddScoped<ISubscriber, Subscriber>();
|
||||
services.AddTransient<IPublisher, Publisher>();
|
||||
services.AddTransient<IMediator, Mediator>();
|
||||
|
||||
services.AddTransient<IValidation, Validation>();
|
||||
services.AddTransient<IValidatorCollection, ValidatorCollection>();
|
||||
|
||||
services.AddScoped<IProxyService<IPublisher>>(provider =>
|
||||
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) =>
|
||||
new(value);
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public static ValidationEventArgs<TValue> As<TValue>() where TValue : new() =>
|
||||
new(new TValue());
|
||||
}
|
||||
public ValidationErrorCollection Errors { get; } = [];
|
||||
|
||||
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,10 +5,11 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class ValidationErrorCollection : IDictionary<string, string>,
|
||||
IDictionary,
|
||||
INotifyCollectionChanged,
|
||||
INotifyPropertyChanged
|
||||
public class ValidationErrorCollection :
|
||||
IDictionary<string, string>,
|
||||
IDictionary,
|
||||
INotifyCollectionChanged,
|
||||
INotifyPropertyChanged
|
||||
{
|
||||
private Dictionary<string, string> items;
|
||||
|
||||
@@ -41,11 +42,7 @@ public class ValidationErrorCollection : IDictionary<string, string>,
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return items[key];
|
||||
}
|
||||
|
||||
get => items.ContainsKey(key) ? items[key] : "";
|
||||
set
|
||||
{
|
||||
bool replace = items.TryGetValue(key, out var old);
|
||||
@@ -54,7 +51,8 @@ public class ValidationErrorCollection : IDictionary<string, string>,
|
||||
if (replace)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public record ValidationEventArgs<TValue>(TValue Value);
|
||||
@@ -1,22 +1,27 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class PropertyValidator
|
||||
public class ValidationRule
|
||||
{
|
||||
public PropertyValidator(Func<bool> validation,
|
||||
public ValidationRule(Func<bool> validation,
|
||||
string message)
|
||||
{
|
||||
Validation = validation;
|
||||
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)
|
||||
{
|
||||
Validation = validation;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public Func<string> Message { get; }
|
||||
public Func<string>? Message { 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
|
||||
{
|
||||
private readonly Action? propertyChanged;
|
||||
private readonly PropertyValidator? propertyValidation;
|
||||
private readonly ValidationRule[] rules = [];
|
||||
|
||||
internal Validator(string propertyName,
|
||||
Action propertyChanged)
|
||||
public Validator(string propertyName,
|
||||
ValidationRule[] rules)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
this.propertyChanged = propertyChanged;
|
||||
}
|
||||
|
||||
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;
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
public string? PropertyName { get; }
|
||||
|
||||
public void Set() => propertyChanged?.Invoke();
|
||||
|
||||
public bool TryValidate([MaybeNull] out string message)
|
||||
{
|
||||
message = "";
|
||||
|
||||
if (propertyValidation is not null && propertyValidation.Validation?.Invoke() == false)
|
||||
foreach (ValidationRule rule in rules)
|
||||
{
|
||||
message = propertyValidation.Message.Invoke();
|
||||
return false;
|
||||
if (rule.Validation?.Invoke() == false)
|
||||
{
|
||||
message = rule.Message?.Invoke();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
propertyChanged?.Invoke();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user