Avalonia低入侵数据验证实现

程序员有二十年 2024-05-22 20:09:00
1.概要

目前在.NET8 Avalonia 11.0.10版本中有三种常见的数据验证方式:

IDataErrorInfo:这是一种较旧的数据验证方式,主要用于WPF和WinForms。在这种方法中,模型需要实现IDataErrorInfo接口,该接口包含一个索引器和一个Error属性。索引器用于获取特定属性的错误消息,Error属性用于获取对象级别的错误消息。

INotifyDataErrorInfo:这是一种更现代的数据验证方式,它允许异步验证并能提供多个错误消息。在这种方法中,模型需要实现INotifyDataErrorInfo接口,该接口包含一个ErrorsChanged事件和两个方法:GetErrors和HasErrors。GetErrors方法用于获取特定属性的错误消息,HasErrors属性用于确定对象是否有任何错误。

还有一种通过FluentValidation库进行数据验证的方式。

FluentValidation是一个流行的.NET库,用于构建强大、可维护的验证规则。它使用流畅的API创建验证规则,支持复杂的验证场景,并可以很好地与Avalonia一起工作。

使用FluentValidation,您可以在单独的验证类中定义规则,这样可以使模型类保持简洁。

在Avalonia中,最佳实践是使用INotifyDataErrorInfo接口进行数据验证。

2.详细内容

在以上的三种验证中我选择的是INotifyDataErrorInfo来实现,看到的一些示例实现的比较麻烦或者不够简洁我做了一些改进。

ValidateBindbleBase

因为INotifyDataErrorInfo需要在每个ViewModel或Model中进行数据验证,所以我将BindableBase和INotifyDataErrorInfo进行封装。这样继承了之后就不需要每次都编写相同的逻辑。

public ValidateBindbleBase : BindableBase, INotifyDataErrorInfo{private readonly Dictionary<string, ICollection<string>> _validationErrors = new Dictionary<string, ICollection<string>>();public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;public bool HasErrors => _validationErrors.Count > 0;public IEnumerable GetErrors(string propertyName) {//If is not returned, there will be two identical exception prompts on the viewreturn ; }protected void ValidateProperty<T>(string propertyName, T value) {if (value == ) return;var context = new ValidationContext(this) { MemberName = propertyName };var validationResults = new List<ValidationResult>(); Validator.TryValidateProperty(value, context, validationResults);if (_validationErrors.ContainsKey(propertyName)) { _validationErrors.Remove(propertyName); } HandleValidationResults(propertyName, validationResults); }private void HandleValidationResults(string propertyName, ICollection<ValidationResult> validationResults) {if (validationResults.Any()) { _validationErrors.Add(propertyName, new List<string>());foreach (var validationResult in validationResults) { _validationErrors[propertyName].Add(validationResult?.ErrorMessage); } ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); } }}ViewModel

这里在ViewModel层做了一个简单的示例,ViewModel继承ValidateBindbleBase之后即获得了数据验证和数据通知的能力。再通过系统或自定义的验证特性进行附加,这样就可以复用验证规则避免出现大量的if else。

public ViewModel : ValidateBindbleBase{private string _packetName; [MinLength(2)] [StringValidator]public string PacketName { get => _packetName;set {if (SetProperty(ref _packetName, value)) ValidateProperty(nameof(PacketName), value); } }}Validation

如果系统的验证特性不能满足需要,那么我们可以通过自定义特性继承ValidationAttribute,通过重写IsValid、FormatErrorMessage来实现自定义验证规则和验证错误提示。

public partial StringValidator : ValidationAttribute{public override string FormatErrorMessage(string name) {return "The field is invaild!"; }public override bool IsValid(object? value) {try {return StringRegex().IsMatch(value?.ToString()); }catch {return false; } } [GeneratedRegex(@"^[a-zA-Z0-9_\-\.\u4e00-\u9fa5]+$")]private static partial Regex StringRegex();}View

在view层正常绑定即可。

<TextBox Grid.Row="2" Grid.Column="1" Margin="5" Text="{Binding CurrnetConfig.PacketName}" />

0 阅读:4

程序员有二十年

简介:感谢大家的关注