much simpler validations
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendControlViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendControlViewModel.cs
@@ -81,6 +81,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
Global = Locator.Current.GetService<Global>();
Wallet = wallet;
+ RegisterValidationMethod(nameof(Address), ValidateActiveAddress);
+
LabelSuggestion = new SuggestLabelViewModel();
BuildTransactionButtonText = DoButtonText;
@@ -377,8 +379,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
IsBusy = false;
}
},
- this.WhenAny(x => x.IsMax, x => x.AmountText, x => x.Address, x => x.IsBusy,
- (isMax, amount, address, busy) => (isMax.Value || !string.IsNullOrWhiteSpace(amount.Value)) && !string.IsNullOrWhiteSpace(Address) && !IsBusy)
+ this.WhenAny(x => x.IsMax, x => x.AmountText, x => x.Address, x => x.IsBusy, x=>x.HasErrors,
+ (isMax, amount, address, busy, hasErrors) => (isMax.Value || !string.IsNullOrWhiteSpace(amount.Value)) && !string.IsNullOrWhiteSpace(Address) && !IsBusy && !HasErrors)
.ObserveOn(RxApp.MainThreadScheduler));
UserFeeTextKeyUpCommand = ReactiveCommand.Create((KeyEventArgs key) =>
@@ -880,29 +882,20 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public ErrorDescriptors ValidatePassword() => PasswordHelper.ValidatePassword(Password);
- public ErrorDescriptors ValidateActiveAddress()
- {
- if (string.IsNullOrWhiteSpace(Address))
+ public void ValidateActiveAddress(AddErrorDelegate addError)
{
- return ErrorDescriptors.Empty;
- }
-
if (Address == CustomChangeAddress)
{
- return new ErrorDescriptors(new ErrorDescriptor(ErrorSeverity.Error, "The active address and the change address cannot be the same."));
- }
-
- if (AddressStringParser.TryParseBitcoinAddress(Address, Global.Network, out _))
- {
- return ErrorDescriptors.Empty;
+ addError(ErrorSeverity.Error, "The active address and the change address cannot be the same.");
+ return;
}
- if (AddressStringParser.TryParseBitcoinUrl(Address, Global.Network, out _))
+ if (!AddressStringParser.TryParseBitcoinAddress(Address, Global.Network, out _) ||
+ !AddressStringParser.TryParseBitcoinUrl(Address, Global.Network, out _))
{
- return ErrorDescriptors.Empty;
+ addError(ErrorSeverity.Error, "Invalid address.");
+ return;
}
-
- return new ErrorDescriptors(new ErrorDescriptor(ErrorSeverity.Error, "Invalid address."));
}
public ErrorDescriptors ValidateCustomChangeAddress()

MODIFY
WalletWasabi.Gui/ViewModels/Validation/Validator.cs
WalletWasabi.Gui/ViewModels/Validation/Validator.cs
@@ -6,75 +6,55 @@ using WalletWasabi.Models;
namespace WalletWasabi.Gui.ViewModels.Validation
{
- public static class Validator
- {
- public static IEnumerable<(string, MethodInfo)> PropertiesWithValidation(object instance)
- {
- foreach (PropertyInfo pInfo in ReflectionHelper.GetPropertyInfos(instance))
- {
- var vma = ReflectionHelper.GetAttribute<ValidateMethodAttribute>(pInfo);
- if (vma is null)
- {
- continue;
- }
+ public delegate void AddErrorDelegate(ErrorSeverity severity, string error);
- var mInfo = ReflectionHelper.GetMethodInfo<ErrorDescriptors>(instance, vma.MethodName);
- yield return (pInfo.Name, mInfo);
- }
- }
+ public delegate void ValidateMethod(AddErrorDelegate addError);
- public static ErrorDescriptors ValidateAllProperties(ViewModelBase viewModelBase, List<(string propertyName, MethodInfo mInfo)> validationMethodCache)
- {
- if (validationMethodCache is null)
+ public static class Validator
{
- return ErrorDescriptors.Empty;
- }
+ //public static IEnumerable<(string propertyName, ErrorDescriptors errors)> ValidateAllProperties(Dictionary<string, ValidateMethod> validationMethodCache)
+ //{
+ // if(validationMethodCache is null || validationMethodCache.Count == 0)
+ // {
+ // throw new Exception("Cant call ValidateAllProperties on ViewModels with no ValidateAttributes");
+ // }
- ErrorDescriptors result = null;
+ // var result = new List<(string propertyName, ErrorDescriptors errors)>();
- foreach (var validationCache in validationMethodCache)
- {
- var invokeRes = (ErrorDescriptors)validationCache.mInfo.Invoke(viewModelBase, null);
+ // foreach (var propertyName in validationMethodCache.Keys)
+ // {
- if (result is null)
- {
- result = new ErrorDescriptors();
- }
+ // var invokeRes = (ErrorDescriptors)validationMethodCache[propertyName].Invoke();
- result.AddRange(invokeRes);
- }
+ // result.Add((propertyName, invokeRes));
+ // }
- return result ?? ErrorDescriptors.Empty;
- }
+ // return result;
+ //}
- public static ErrorDescriptors ValidateProperty(ViewModelBase viewModelBase, string propertyName,
- List<(string propertyName, MethodInfo mInfo)> validationMethodCache)
- {
- if (validationMethodCache is null)
- {
- return ErrorDescriptors.Empty;
- }
+ //public static ErrorDescriptors ValidateProperty(ViewModelBase viewModelBase, string propertyName,
+ // Dictionary<string, MethodInfo> validationMethodCache)
+ //{
+ // if (validationMethodCache is null)
+ // {
+ // return ErrorDescriptors.Empty;
+ // }
- ErrorDescriptors result = null;
+ // ErrorDescriptors result = null;
- foreach (var validationCache in validationMethodCache)
- {
- if (validationCache.propertyName != propertyName)
- {
- continue;
- }
-
- var invokeRes = (ErrorDescriptors)validationCache.mInfo.Invoke(viewModelBase, null);
+ // if(validationMethodCache.ContainsKey(propertyName))
+ // {
+ // var invokeRes = (ErrorDescriptors)validationMethodCache[propertyName].Invoke(viewModelBase, null);
- if (result is null)
- {
- result = new ErrorDescriptors();
- }
+ // if (result is null)
+ // {
+ // result = new ErrorDescriptors();
+ // }
- result.AddRange(invokeRes);
- }
+ // result.AddRange(invokeRes);
+ // }
- return result ?? ErrorDescriptors.Empty;
- }
+ // return result ?? ErrorDescriptors.Empty;
+ //}
}
}

MODIFY
WalletWasabi.Gui/ViewModels/ViewModelBase.cs
WalletWasabi.Gui/ViewModels/ViewModelBase.cs
-using Newtonsoft.Json;
using ReactiveUI;
using System;
using System.Collections;
@@ -11,38 +10,95 @@ using WalletWasabi.Models;
namespace WalletWasabi.Gui.ViewModels
{
- public class ViewModelBase : ReactiveObject, INotifyDataErrorInfo
+ public class ViewModelBase : ReactiveObject
{
+ private Dictionary<string, ErrorDescriptors> _errorsByPropertyName;
+ private Dictionary<string, ValidateMethod> _validationMethods;
+
public ViewModelBase()
{
- var vmc = Validator.PropertiesWithValidation(this).ToList();
- if (vmc.Count == 0)
+ _errorsByPropertyName = new Dictionary<string, ErrorDescriptors>();
+ _validationMethods = new Dictionary<string, ValidateMethod>();
+
+ PropertyChanged += ViewModelBase_PropertyChanged;
+ }
+
+ protected void RegisterValidationMethod(string propertyName, ValidateMethod validateMethod)
+ {
+ if (string.IsNullOrWhiteSpace(propertyName))
{
- return;
+ throw new ArgumentException("PropertyName must be valid.", nameof(propertyName));
}
- ValidationMethodCache = vmc;
+ _validationMethods[propertyName] = validateMethod;
}
- public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
+ private void ViewModelBase_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(e.PropertyName))
+ {
+ foreach (var propertyName in _validationMethods.Keys)
+ {
+ ValidateProperty(propertyName);
+ }
+ }
+ else
+ {
+ ValidateProperty(e.PropertyName);
+ }
+ }
- private List<(string propertyName, MethodInfo mInfo)> ValidationMethodCache { get; }
+ private void ValidateProperty(string propertyName)
+ {
+ if (_validationMethods.ContainsKey(propertyName))
+ {
+ ClearErrors(propertyName);
- public bool HasErrors => Validator.ValidateAllProperties(this, ValidationMethodCache).HasErrors;
+ _validationMethods[propertyName]((severity, error) =>
+ {
+ AddError(propertyName, severity, error);
+ });
+ }
+ }
+
+ public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
+
+ public bool HasErrors => _errorsByPropertyName.Where(x => x.Value.HasErrors).Any();
public IEnumerable GetErrors(string propertyName)
{
- var error = Validator.ValidateProperty(this, propertyName, ValidationMethodCache);
+ return _errorsByPropertyName.ContainsKey(propertyName) && _errorsByPropertyName[propertyName].HasErrors
+ ? _errorsByPropertyName[propertyName]
+ : ErrorDescriptors.Empty;
+ }
- if (error.HasErrors)
+ private void AddError(string propertyName, ErrorSeverity severity, string error)
{
- return error;
+ if (!_errorsByPropertyName.ContainsKey(propertyName))
+ {
+ _errorsByPropertyName[propertyName] = new ErrorDescriptors();
+ }
+
+ _errorsByPropertyName[propertyName].Add(new ErrorDescriptor(severity, error));
+
+ OnErrorsChanged(propertyName);
+
+ //this.RaisePropertyChanged(nameof(HasErrors));
}
- return ErrorDescriptors.Empty;
+ private void ClearErrors(string propertyName)
+ {
+ if (_errorsByPropertyName.ContainsKey(propertyName))
+ {
+ _errorsByPropertyName.Remove(propertyName);
+
+ OnErrorsChanged(propertyName);
+
+ //this.RaisePropertyChanged(nameof(HasErrors));
+ }
}
- protected void NotifyErrorsChanged(string propertyName)
+ private void OnErrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}

much simpler validations